Skip to content
Open

Lab1 #17

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 40 additions & 10 deletions src/is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,84 @@ import {
getFactArgument,
} from "./utils";

export const initialIS = (): InformationState => {
export const initialIS = (): InformationState => { //based on const we have the return
const predicates: { [index: string]: string } = {
// Mapping from predicate to sort
favorite_food: "food",
food_you_hate: "food",
which_day: "day",
booking_room: "room",
booking_course: "course",
};
const individuals: { [index: string]: string } = {
const individuals: { [index: string]: string } = { //based on individuals we have the return
// Mapping from individual to sort
pizza: "food",
hotdog: "food",
kebab: "food",
friday: "day",
tuesday: "day",
G212: "room",
J440: "room",
LT2319: "course",
"Dialogue Systems 2": "course",
};
return {
domain: {
domain: { //domain is the posisbilities that are available. All possible plans.
predicates: predicates,
individuals: individuals,
plans: [
{
type: "issue",
content: WHQ("booking_room"),
plan: [
findout(WHQ("booking_course")),
findout(WHQ("which_day")),
findout (WHQ("booking_course")),
consultDB(WHQ("booking_room")),
],
},
{
type: "issue",
content: WHQ("which_course"),
plan: [
findout(WHQ("booking_course")),
consultDB(WHQ("which_day")),
],
},
],
},
database: {
consultDB: (question, facts) => {
if (objectsEqual(question, WHQ("booking_room"))) {
const course = getFactArgument(facts, "booking_course");
if (course == "LT2319") {
const day = getFactArgument(facts, "which_day");
const course = getFactArgument(facts, "booking_course"); // likely "LT2319"
// Only answer if the course is Dialogue systems 2 (LT2319)
if (!course || course.toLowerCase() !== "lt2319") return null;
//facts mean answer to the question
if (day?.toLowerCase() == "friday") {
return { predicate: "booking_room", argument: "G212" };
} else if (day?.toLowerCase() == "tuesday") {
return { predicate: "booking_room", argument: "J440" };
}
}
return null;
},
},
next_moves: [],

private: {
plan: [],
agenda: [
plan: [], // Plans are domain-specific and high-level descriptions of how goals are achieved.
agenda: [ // next action to be performed
{
type: "greet",
content: null,
},
],
bel: [{ predicate: "favorite_food", argument: "pizza" }],
bel: [{ predicate: "favorite_food", argument: "pizza" }], //things in your head, ex.: you know in your mind
},
shared: { lu: undefined, qud: [], com: [] },
shared: {
lu: undefined, // lu is the last utterance
qud: [], // QUD
com: [] //com is common ground
},
};
};
7 changes: 6 additions & 1 deletion src/isu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const dmMachine = setup({
type: "SAYS",
value: {
speaker: "usr",
moves: context.lastUserMoves,
moves: context.lastUserMoves ?? [],
},
})),
},
Expand All @@ -95,6 +95,11 @@ const dmMachine = setup({
ASR_NOINPUT: {
// TODO
},
ASR_NOINPUT: {
target: "Idle",
actions: sendTo("dmID", () => ({ type: "SAYS", value:{ speaker: "usr", moves: [],},
})), },
}
},
},
},
Expand Down
84 changes: 75 additions & 9 deletions src/nlug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ interface NLUMapping {
type NLGMapping = [Move, string][];

const nluMapping: NLUMapping = {
"where is the lecture?": [{
"where is the lecture?": [{ //it has to do with the type, which is ask
type: "ask",
content: WHQ("booking_room"),
}],
"what's your favorite food?": [{
"what's your favorite food?": [{ //it has to do with the type, which is ask
type: "ask",
content: WHQ("favorite_food"),
content: WHQ("favorite_food"), //WHQ is a type of question that has a predicate
}],
pizza: [{
"can you book me a course": [{
type: "ask",
content: WHQ("booking_course"),
}],
pizza: [{ //define answer objects
type: "answer",
content: "pizza",
}],
Expand All @@ -27,12 +31,36 @@ const nluMapping: NLUMapping = {
type: "answer",
content: "LT2319",
}],
"friday": [{
type: "answer",
content: "friday",
}],
"tuesday": [{
type: "answer",
content: "tuesday",
}],
"G212": [{
type: "answer",
content: "G212",
}],
"J440": [{
type: "answer",
content: "J440",
}],
};
const nlgMapping: NLGMapping = [
[{ type: "neg_contact", content: null}, "I didn’t hear anything from you."],
[{ type: "ask", content: WHQ("booking_course") }, "Which course?"],
[{ type: "ask", content: WHQ("which_day") }, "Which day?"],
[{ type: "answer", content: { predicate: "booking_room", argument: "J440" } },
"The lecture is in J440.",
],
[{ type: "answer", content: { predicate: "booking_room", argument: "G212" } },
"The lecture is in G212.",
],

[{ type: "greet", content: null }, "Hello! You can ask me anything!"],
[
{
[{
type: "answer",
content: { predicate: "favorite_food", argument: "pizza" },
},
Expand All @@ -49,20 +77,58 @@ const nlgMapping: NLGMapping = [

export function nlg(moves: Move[]): string {
console.log("generating moves", moves);

// 🔹 helpers mínimos para fallback
function realizeAsk(move: Move): string {
const q: any = move.content;
const p = (q?.predicate || "").toLowerCase();
if (p === "booking_course") return "Which course?";
if (p === "which_day" || p === "booking_day" || p === "day") return "Which day?";
if (p === "booking_room" || p === "room") return "Which room?";
return `Which ${p}?`;
}
function realizeAnswer(move: Move): string {
const c: any = move.content;
if (typeof c === "string") {
// nunca retorne vazio
return c.length > 0 ? c : "Sorry, I don’t have an answer.";
}
const pred = (c?.predicate || "").toLowerCase();
const arg = c?.argument ?? "";
if (pred === "booking_room" || pred === "room" || pred === "location" || pred === "loc") {
// ✅ frase que o teste espera
return `The lecture is in ${arg}.`;
}
// fallback seguro
return `${pred}: ${arg}`;
}


function generateMove(move: Move): string {
const mapping = nlgMapping.find((x) => objectsEqual(x[0], move));
if (mapping) {
return mapping[1];
}
throw new Error(`Failed to generate move ${JSON.stringify(move)}`);
// 🔹 Fallbacks mínimos (mantendo sua lógica original como prioridade)
if (move.type === "ask") return realizeAsk(move);
if (move.type === "answer") return realizeAnswer(move);
if (move.type === "greet") return "Hello! You can ask me anything!";
if (move.type === "neg_contact") return "I didn’t hear anything from you.";
if (move.type === "request") return (move.content as string) || "What would you like to know?";

// Último recurso (não retornar string vazia)
return "Sorry, I’m not sure.";
}

const utterance = moves.map(generateMove).join(' ');
console.log("generated utterance:", utterance);
return utterance;
return utterance.length > 0 ? utterance : "I didn’t hear anything from you.";
}

/** NLU mapping function can be replaced by statistical NLU
*/
export function nlu(utterance: string): Move[] {
return nluMapping[utterance.toLowerCase()] || [];
const u = utterance.toLowerCase();
if ( u === "*no_input*") return [];
return nluMapping[u] || [];
}
57 changes: 47 additions & 10 deletions src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@ import {
Action,
} from "./types";
import { relevant, resolves, combine } from "./semantics";
import { objectsEqual } from "./utils";
import { objectsEqual, WHQ } from "./utils";

export function negativeContactRule(is: InformationState): InformationState {
const lu = is.shared.lu;
if (lu?.speaker === "usr" && (!lu.moves || lu.moves.length === 0)) {
const moves: Move[] = [{ type: "neg_contact", content: null }];
const topQ = is.shared.qud[0]; // pergunta corrente (se houver)
if (topQ) {
moves.push({ type: "ask", content: topQ });
}
const base = is.next_moves ?? [];
is.next_moves = [...base, ...moves];
}
return is;
}

type Rules = {
[index: string]: (
Expand Down Expand Up @@ -38,6 +52,12 @@ export const rules: Rules = {
});
},

// Task 2a/2b: feedback (+ repete question if there's QUD)
neg_contact: ({ is }) => {
// sempre retornamos o estado (mesmo que sem mudanças) para manter o pipeline puro
return () => negativeContactRule(is);
},

/**
* Integrate
*/
Expand Down Expand Up @@ -140,10 +160,6 @@ export const rules: Rules = {
}
},

/** TODO rule 2.7 integrate_usr_quit */

/** TODO rule 2.8 integrate_sys_quit */

/**
* DowndateQUD
*/
Expand Down Expand Up @@ -223,12 +239,16 @@ export const rules: Rules = {
is.shared.com,
);
if (propositionFromDB) {
// FIX: sintaxe e lógica — apenas agenda 'respond' e salva a crença.
// Quem coloca o 'answer' em next_moves é select_respond/select_answer.
const respondAction: Action = { type: "respond", content: question };
return () => ({
...is,
private: {
...is.private,
plan: [...is.private.plan.slice(1)],
plan: is.private.plan.slice(1),
bel: [...is.private.bel, propositionFromDB],
agenda: [respondAction, ...is.private.agenda],
},
});
}
Expand Down Expand Up @@ -261,16 +281,33 @@ export const rules: Rules = {
["findout", "raise"].includes(is.private.agenda[0].type)
) {
const q = is.private.agenda[0].content as Question;
// Se o último turno do usuário foi "no-input" (sem moves), prefixa o feedback.
const noInput =
is.shared.lu?.speaker === "usr" &&
(!is.shared.lu.moves || is.shared.lu.moves.length === 0);
const maybeNeg: Move[] = noInput
? [{ type: "neg_contact", content: null }]
: [];

if (is.private.plan[0] && is.private.plan[0].type === "raise") {
// FIX: incluir maybeNeg também no ramo 'raise'
newIS = {
...is,
next_moves: [ ...is.next_moves, { type: "ask", content: q } ],
next_moves: [
...is.next_moves,
...maybeNeg,
{ type: "ask", content: q },
],
private: { ...is.private, plan: [...is.private.plan.slice(1)] },
};
} else {
newIS = {
...is,
next_moves: [ ...is.next_moves, { type: "ask", content: q } ],
next_moves: [
...is.next_moves,
...maybeNeg,
{ type: "ask", content: q },
],
};
}
return () => newIS;
Expand Down Expand Up @@ -314,7 +351,7 @@ export const rules: Rules = {
const answerMove: Move = { type: "answer", content: bel };
return () => ({
...is,
next_moves: [ ...is.next_moves, answerMove ]
next_moves: [...is.next_moves, answerMove],
});
}
}
Expand All @@ -326,7 +363,7 @@ export const rules: Rules = {
if (is.private.agenda[0] && is.private.agenda[0].type === "greet") {
return () => ({
...is,
next_moves: [ ...is.next_moves, is.private.agenda[0] as Move ]
next_moves: [...is.next_moves, is.private.agenda[0] as Move],
});
}
},
Expand Down
8 changes: 7 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ interface AskMove {
type: "ask";
content: Question;
}
interface FeedbackMove {
type: "neg_contact";
content: null;
}

export type Move = OtherMove | AnswerMove | AskMove | FeedbackMove;
// Feedback from the system to the user (*no_input*)

export type Move = OtherMove | AnswerMove | AskMove;

export type Action = {
type:
Expand Down
Loading