Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

206
LINES

< > BotCompany Repo | #1027518 // Chat Bot Frontend [with command translations, e.g. for Scenarios]

JavaX source code (Dynamic Module) [tags: use-pretranspiled] - run with: Stefan's OS

Uses 911K of libraries. Click here for Pure Java version (17189L/92K).

!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<Cmd> {
  switchable S backendModuleLibID = "#1027443/Scenarios";
  O currentAttractor;
  transient CRUD<Replacement> replacementsCRUD = new(Replacement);
  transient CRUD<Mishearing> mishearingsCRUD = new(Mishearing);
  
  class HandleArgument implements IF1<S> {
    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<Bool, S> {
    public S get(Bool yes) {
      if (!yes) ret "OK"; // user said no
      ret (S) sendToBackend("undo");
    }
  }

  start {
    dm_watchFieldAndNow backendModuleLibID(r {
      dm_setModuleName("Frontend for " + dm_moduleName(backend()))
    });
    
    crud.multiLineFields = litset("exampleInputs", "questionsForArguments");
    crud.sorter = func(Cl<Cmd> l) -> L<Cmd> { 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<Cmd> cmds = list(Cmd);
    L<MMOConsistencyError> 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<Cmd> 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;
  }
}

download  show line numbers  debug dex  old transpilations   

Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1027518
Snippet name: Chat Bot Frontend [with command translations, e.g. for Scenarios]
Eternal ID of this version: #1027518/86
Text MD5: 8ee6614dcc555cedfe6a2fed609a73bf
Transpilation MD5: ef037be1a965005ac41b40bdd80ea89f
Author: stefan
Category: javax
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-03-24 11:29:17
Source code size: 6365 bytes / 206 lines
Pitched / IR pitched: No / No
Views / Downloads: 254 / 1528
Version history: 85 change(s)
Referenced in: #1027443 - Scenarios DB [use with frontend #1027518]
#1027566 - Chat Bot Frontend [backup v1]
#1027602 - Chat Bot Frontend v2 [with case ID]