Warning: session_start(): open(/var/lib/php/sessions/sess_b975smd5r6ueps4kutf61p04lc, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
!7
concept Cmd {
S patterns; // for mmo_parsePattern
S exampleInputs; // line-separated
S cmd; // a star pattern recognized by the backend
S questionsForArguments; // line-separated
S conditions;
int index;
bool autoGenerated;
LS translatableFields() { ret splitAtSpace("patterns exampleInputs cmd questionsForArguments"); }
transient MMOPattern parsedPattern;
void change { parsedPattern = null; super.change(); }
MMOPattern parsedPattern() {
if (parsedPattern == null) parsedPattern = mmo_parsePattern(patterns);
ret parsedPattern;
}
sS _fieldOrder = "autoGenerated exampleInputs patterns cmd questionsForArguments conditions";
}
concept Replacement {
S in, out; // word to replace and which word to replace it with
S except; // IDs of Cmd concepts to skip
sS _fieldOrder = "in out except";
}
concept Mishearing {
S in, out;
}
cmodule ChatBotFrontend > DynCRUD {
switchable S backendModuleLibID = "#1027443/Scenarios";
O currentAttractor;
transient CRUD replacementsCRUD = new(Replacement);
transient CRUD mishearingsCRUD = new(Mishearing);
class HandleArgument implements IF1 {
S cmd;
LS argQuestions;
new LS arguments;
*() {}
*(S *cmd, LS *argQuestions) {}
// process an argument
public S get(S arg) {
arguments.add(arg);
if (l(arguments) >= countAsteriskTokens(cmd))
ret sendToBackend(format_quoteAll(cmd, asObjectArray(arguments)));
else
ret handleQuestionWithArguments(this);
}
S currentQuestion() {
ret or2(_get(argQuestions, l(arguments)), "Need argument");
}
}
runnable class Cancel { setField(currentAttractor := null); }
class UndoHandler implements IF1 {
public S get(Bool yes) {
if (!yes) ret "OK"; // user said no
ret (S) sendToBackend("undo");
}
}
void start {
print("Case ID: " + caseID);
Pair p = persistentConcepts(programID(), caseID);
cc = p.a;
ownResource(p.b);
super.start();
dm_watchFieldAndNow backendModuleLibID(r {
dm_setModuleName("Frontend for " + dm_moduleName(backend()))
});
crud.multiLineFields = litset("exampleInputs", "questionsForArguments");
crud.sorter = func(Cl l) -> L { sortByField index(l) };
crud.formFixer = 48;
}
S handleCommand(Cmd cmd) {
if (cmd == null) null;
print("handleCommand " + cmd.cmd);
if (match("undo after confirm", cmd.cmd)) {
O undo = dm_call(backend(), 'lastUndo);
if (undo == null) ret "Nothing to undo";
new WaitForAnswer_YesNo attractor;
attractor.question = "Undo " + undo + "?";
setField(currentAttractor := attractor);
print("Set attractor to: " + attractor);
attractor.processAnswer = new UndoHandler;
attractor.cancelSilently = new Cancel;
ret attractor.question;
}
if (hasAsteriskTokens(cmd.cmd))
ret handleQuestionWithArguments(
new HandleArgument(cmd.cmd, tlft(cmd.questionsForArguments)));
else
ret sendToBackend(cmd.cmd);
}
S handleQuestionWithArguments(HandleArgument handler) {
new WaitForName attractor;
attractor.question = handler.currentQuestion();
setField(currentAttractor := attractor);
print("Set attractor to: " + attractor);
attractor.processName = handler;
attractor.cancelSilently = new Cancel;
ret attractor.question;
}
S sendToBackend(S cmd) {
ret (S) dm_call(backend(), 'answer, cmd);
}
S backend() {
ret dm_require(backendModuleLibID);
}
visual {
JComponent mainCRUD = super.visualize();
addComponents(crud.buttons,
jbutton("Talk to me", rThread talkToMe),
jPopDownButton_noText(
"Apply replacements", rThread applyReplacements,
"Consistency check", rThread consistencyCheck));
ret jtabs(
"Commands" := mainCRUD,
"Replacements" := replacementsCRUD.visualize(),
"Mishearings" := mishearingsCRUD.visualize());
}
void talkToMe enter { dm_showConversationPopupForModule(); }
void consistencyCheck enter {
reindex();
L cmds = list(Cmd);
L errors = mmo_consistencyCheck(
map(cmds, cmd -> pair(cmd.exampleInputs, cmd.patterns)));
if (empty(errors)) infoBox("No consistency problems");
else
dm_showText(n2(errors, "consistency problem"), lines(errors));
}
void reindex {
int index = 1;
for (Cmd cmd : conceptsSortedByFields Cmd('autoGenerated, 'index))
cset(cmd, index := index++);
}
void applyReplacements {
deleteConceptsWhere Cmd(autoGenerated := true);
for (Cmd cmd)
for (Replacement r) {
if (jcontains(r.except, str(cmd.id))) continue;
SS map = litcimap(r.in, r.out, plural(r.in), plural(r.out));
Cmd cmd2 = shallowCloneUnlisted(cmd);
cset(cmd2, autoGenerated := true);
for (S field : cmd.translatableFields())
cset(cmd2, field, fixAOrAn(replacePhrases(map, getString(cmd, field))));
if (anyFieldsDifferent(cmd, cmd2, cmd.translatableFields()))
registerConcept(cmd2);
}
consistencyCheck();
}
// API
// for initial fill of translation table. probably doesn't catch many patterns usually
void copyCommandsFromBackend() {
for (S cmd : allIfQuotedPatterns(loadSnippet(beforeSlash(backendModuleLibID))))
uniqConcept(+cmd);
}
S answer(S s) {
S s1 = s;
s = replacePhrases(fieldToFieldIndexCI('in, 'out, list(Mishearing)), s);
if (neq(s, s1)) print("Corrected to: " + s);
if (currentAttractor != null) {
print("Sending to attractor " + currentAttractor + ": " + s);
S a = strOrNull(call(currentAttractor, 'answer, s));
if (a != null) {
print("Attractor said: " + a);
ret a;
}
}
L cmds = filter(conceptsSortedByField Cmd('index),
c -> nempty(c.patterns) && checkCondition(c));
Cmd cmd = mmo_matchMultiWithTypos(1, cmds, c -> c.parsedPattern(), s);
if (cmd != null) ret handleCommand(cmd);
ret sendToBackend(s);
}
bool checkCondition(Cmd cmd) {
if (match("have scenario", cmd.conditions))
ret print("have scenario", dm_call(backend(), 'selectedScenario)) != null;
true;
}
}