Package edu.neu.ccs.task.agent

Source Code of edu.neu.ccs.task.agent.AgentConsole

package edu.neu.ccs.task.agent;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;

import org.mozilla.javascript.RhinoException;

import edu.neu.ccs.task.DecompositionType;
import edu.neu.ccs.task.Plan;
import edu.neu.ccs.task.Priority;
import edu.neu.ccs.task.Task;
import edu.neu.ccs.task.TaskEngine;
import edu.neu.ccs.task.TaskModel;
import edu.neu.ccs.task.TaskModelSet;
import edu.neu.ccs.task.TaskPlan;
import edu.neu.ccs.task.TaskType;
import edu.neu.ccs.task.util.Console;
import edu.neu.ccs.task.util.Decorator;
import edu.neu.ccs.task.util.Desc;
import edu.neu.ccs.task.util.DotUtils;
import edu.neu.ccs.task.util.Dottable;
import edu.neu.ccs.task.util.Help;
import edu.neu.ccs.task.util.Pair;
import edu.neu.ccs.task.util.XMLUtil;

/**
* A console-based interface to the dialogue agent.
*
* @author Dan Schulman
* @version $Id: AgentConsole.java 949 2010-09-12 19:13:24Z schulman $
*/
public class AgentConsole extends Console {
  private final Agent agent;
  private boolean verbose = false;
  private boolean autostatus = false;
 
  /**
   * Create a new agent console, writing to the specified log file.
   *
   * @param logFile path to a log file.
   * @throws IOException
   */
  public AgentConsole(String logfile) throws IOException {
    super(logfile);
    this.agent = new Agent();
  }
 
  public AgentConsole(String logfile, TaskEngine engine) throws IOException {
    super(logfile);
    this.agent = new Agent(engine);
  }
 
  protected Agent getAgent() {
    return agent;
  }
 
  protected TaskEngine getEngine() {
    return agent.getEngine();
  }
 
  protected TaskModelSet getModelSet() {
    return agent.getModelSet();
  }
 
  @Override
  public void run() throws IOException {
    printBanner();
    super.run();
  }
 
  protected void printBanner() {
    out.println("DTask dialogue engine");
    out.println("Version " + Agent.VERSION);
    out.println("-----------------------")
  }
 
  @Override
  protected void system() {
    while (!agent.hasCurrentTurn() && agent.isLive()) {
      boolean hasTurn = agent.nextTurn();
      showAutostatus();
      if (hasTurn)
        performTurn();
    }
  }
 
  private void performTurn() {
    String output = verbose ?
      agent.getCurrentAnnotatedOutput() :
      agent.getCurrentPlainOutput();
    if (output != null)
      out.println("AGENT: " + output);
   
    if ( ! showUserInputRequest()) {
      agent.applyCurrentTurn();
      showAutostatus();
    }
  }

  private void performRepeat() {
    String output = verbose ?
      agent.getCurrentAnnotatedAltOutput() :
      agent.getCurrentPlainAltOutput();
    out.println("AGENT: " + output);
   
    showUserInputRequest();
  }
 
  private boolean showUserInputRequest() {
    List<Pair<String,Integer>> inputs = agent.getCurrentInputs();
    if (inputs != null) {
      showUserChoices(inputs);
      return true;
    }
   
    String prompt = agent.getCurrentInputPrompt();
    if (prompt != null) {
      showUserPrompt(prompt);
      return true;
    }
   
    Pair<String, Map<String,String>> widget= agent.getCurrentInputWidget();
    if (widget != null) {
      showUserWidget(widget);
      return true;
    }
   
    return false;
  }
 
  private void showUserChoices(List<Pair<String, Integer>> inputs) {
    int i=1;
    for (Pair<String, Integer> p : inputs)
      out.printf("%d. %s\n", i++, p.first);
  }
 
  private void showUserPrompt(String prompt) {
    out.printf("TEXTINPUT: %s\n", prompt);
  }
 
  private void showUserWidget(Pair<String, Map<String,String>> w) {
    out.printf("WIDGET: %s\n", w.first);
    for (Map.Entry<String, String> e : w.second.entrySet())
      out.printf("%s=%s\n", e.getKey(), e.getValue());
  }
 
  @Override
  protected void reportException(Exception e) {
    // Try to do a good job of reporting javascript exceptions.
    // We get better messages if we look for the Rhino-specific exceptions
    for (Throwable t = e; t != null; t = t.getCause())
      if (t instanceof RhinoException) {
        RhinoException re = (RhinoException) t;
        out.println("Javascript error: " + re.getMessage());
        return;
      }
    super.reportException(e);
  }
 
  @Desc("evaluate a Javascript expression")
  @Help("Usage: eval EXPR\n" +
      "EXPR is evaluated as a Javascript expression, and the\n" +
      "results are printed.  Evaluation is in the global \n" +
      "environment, so $this is not bound.")
  public void _eval(String expr) {
    TaskEngine e = getEngine();
    Object value = e.evalGlobal(expr, "console eval");
    if (value != null)
      out.println(e.asLiteral(value, "console eval"));
  }
 
  @Desc("load a model file")
  @Help("Usage: load FILE\n" +
      "Usage: load PREFIX=FILE\n" +
      "A task model is loaded from FILE, which may be either a path\n" +
      "or a URL.  If PREFIX is supplied, then this prefix is bound\n" +
      "to the model's URI, and can be used to refer to any tasks or\n" +
      "task decompositions within in.  If no prefix is supplied, a\n" +
      "default one will be created.\n" +
      "Loaded a model file causes any initialization scripts in the\n" +
      "model to be executed.\n" +
      "If a model file with the same URI has already been loaded,\n" +
      "the existing model file will first be unloaded.")
  public void _load(String model) throws IOException, XMLStreamException {
    if ( ! XMLUtil.validate(model, XMLUtil.getDialogueSchema()))
      return;
   
    String prefix = null;
   
    String[] parts = model.split("=", 2);
    if (parts.length == 2) {
      prefix = parts[0].trim();
      model = parts[1].trim();
    }
   
    TaskModel m = getEngine().loadAndInit(model, prefix);
    if (m.getLoadCount() > 1)
      out.printf("Reloaded %s=%s\n", m.getPrefix(), m.getURI());
    else
      out.printf("Loaded %s=%s\n", m.getPrefix(), m.getURI());
  }
 
  @Desc("remove a task model from the runtime")
  @Help("Usage: unload PREFIX\n" +
      "The model bound to PREFIX is removed from the runtime.\n" +
      "All tasks, decompositions, scripts, and dialogue contained in\n" +
      "the model will no longer be accessible.\n" +
      "Note that any existing task instances in the current plan tree\n" +
      "will still be present -- however new instances cannot be\n" +
      "created")
  public void _unload(String prefix) {
    if ("".equals(prefix))
      prefix = getModelSet().getDefaultPrefix();
   
    TaskModel model = getModelSet().getModelForPrefix(prefix);
    if (model == null)
      throw new IllegalArgumentException("unknown model: " + prefix);
   
    getModelSet().unload(model.getURI());
    out.printf("Unloaded %s=%s\n", prefix, model.getURI());
  }
 
  @Desc("reload a task model from its original location")
  @Help("Usage: reload\n" +
      "Usage: reload PREFIX\n" +
      "Reload the model bound to PREFIX, or the current default\n" +
      "model, if no PREFIX is supplied.\n" +
      "This is equivalent to unloading a model, following by\n" +
      "loading it again (from the same location as before).\n" +
      "Note that any task instances in the current plan tree\n" +
      "will continue to refer to the previous copy of the model.")
  public void _reload(String prefix) throws IOException, XMLStreamException {
    if ("".equals(prefix))
      prefix = getModelSet().getDefaultPrefix();
   
    TaskModel model = getModelSet().getModelForPrefix(prefix);
    if (model == null)
      throw new IllegalArgumentException("unknown model: " + prefix);
   
    _load(model.getLocation());
  }
 
  @Desc("set the default model")
  @Help("Usage: model PREFIX\n" +
      "The model bound to PREFIX becomes the default model.\n" +
      "This means that the tasks contained in this model may\n" +
      "be used in commands such as 'run' without specifying a\n" +
      "prefix.")
  public void _model(String prefix) {
    TaskModel model = getModelSet().getModelForPrefix(prefix);
    if (model == null)
      throw new IllegalArgumentException("unknown model: " + prefix);
   
    getModelSet().setDefaultPrefix(prefix);
    out.printf("Using %s=%s\n", prefix, model.getURI());
  }
 
  @Desc("display loaded models")
  @Help("Usage: models\n" +
      "Lists all loaded models, and the prefix for each.")
  public void _models(String ignored) {
    if (getModelSet().getModels().isEmpty())
      out.println("no models loaded");
   
    for (TaskModel m : getModelSet().getModels()) {
      if (m.getPrefix().equals(getModelSet().getDefaultPrefix()))
        out.print('*'); // highlight the current default namespace
      out.printf("%s=%s\n", m.getPrefix(), m.getURI());
    }
  }
 
  @Desc("start a new top-level task")
  @Help("Usage: run TASK (; INPUT=VALUE)*\n" +
      "Create a new instance of TASK, and start it as a top-level\n" +
      "task, replacing the current plan tree with one rooted at the\n" +
      "new instance.\n" +
      "TASK will be found in the current default model, unless a \n" +
      "prefix is supplied (see 'model').\n" +
      "For each INPUT=VALUE provided, VALUE will be evaluated as a\n" +
      "Javascript expression, and this value will be bound to the\n" +
      "input slot identified by INPUT.  It is an error to leave any\n" +
      "input slots unbound.")
  public void _run(String str) {
    Map<String, String> slots = new HashMap<String, String>();
    String name = parseTask(str, slots);
   
    TaskPlan plan = getEngine().newTaskPlan(
      XMLUtil.parseQName(getModelSet(), name));
    Task task = plan.getTask();
    for (Map.Entry<String, String> e : slots.entrySet())
      task.setSlotValueScript(e.getKey(), e.getValue(), "run task");
   
    if (plan.isStartable())
      agent.setFocus(plan);
    else {
      List<String> undef = task.getUndefinedInputs();
      if (undef.isEmpty())
        out.println("Cannot run " + task + ": precondition not met");
      else
        out.println("Undefined input: " + undef);
    }
  }
 
  private static String parseTask(String s, Map<String, String> slots) {
    String[] args = s.split(";");
 
    for (int n=1; n<args.length; n++) {
      String[] parts = args[n].split("=", 2);
      if (parts.length != 2)
        throw new IllegalArgumentException("Bad argument: " + args[n]);
      slots.put(parts[0].trim(), parts[1]);
    }
   
    return args[0].trim();
  }
 
  @Desc("choose user dialogue, or display possible choices")
  @Help("Usage: say\n" +
      "Display possible choices of user dialogue.\n" +
      "Usage: say NUMBER\n" +
      "Choose a user utterance, where NUMBER is one of the choices\n" +
      "displayed by 'say' with no argument.")
  public void _say(String choiceStr) {
    // First try it as a numeric choice:
    int choice;
    try {
      choice = Integer.parseInt(choiceStr) - 1;
    } catch (NumberFormatException e) {
      choice = -1;
    }
   
    List<Pair<String, Integer>> inputs = agent.getCurrentInputs();
    if ((inputs != null) && (choice >= 0) && (choice<inputs.size())) {
      int value = inputs.get(choice).second;
      if (value < 0)
        performRepeat();
      else {
        agent.applyCurrentTurn(value);
        showAutostatus();
      }
      return;
    }
   
    if (agent.getCurrentInputPrompt()!=null || agent.getCurrentInputWidget()!=null) {
      agent.applyCurrentTurn(choiceStr);
      showAutostatus();
      return;
    }
   
    if ( ! showUserInputRequest())
      out.println("not expecting user input");
  }
 
  @Desc("display the current plan tree")
  @Help("Usage: status\n" +
      "Display a text representation of the current plan tree.\n" +
      "If 'verbose' is enabled, this will contain more detail.")
  public void _status(String ignored) {
    Plan top = agent.getTop();
    if (top == null)
      out.println("No current task");
    else
      top.printTree(out, verbose, new Decorator() {
        public String decorate(Object o) {
          return (o == agent.getFocus()) ? " <-focus" : "";
        }
      });
  }
 
  @Desc("display the current plan tree at each change")
  @Help("Usage: autostatus (true | false)\n" +
      "When autostatus mode is on, display the current plan tree\n" +
      "(as with status) each time it changes.")
  public void _autostatus(String arg) {
    arg = arg.trim();
    autostatus = "".equals(arg) || Boolean.parseBoolean(arg);
    out.println("Autostatus " + (autostatus ? "on" : "off"));
  }
 
  private void showAutostatus() {
    if (autostatus) {
      out.println();
      _status(null);
    }
  }
 
  @Desc("display additional detail")
  @Help("Usage: verbose (true | false)\n" +
      "When verbose mode is on, some commands (notable 'status')\n" +
      "display additional detail.")
  public void _verbose(String arg) {
    arg = arg.trim();
    verbose = "".equals(arg) || Boolean.parseBoolean(arg);
    out.println("Verbose " + (verbose ? "on" : "off"));
  }
 
  @Desc("create a decomposition graph")
  @Help("Usage: dotdecomp TASK FILE\n" +
      "Usage: dotdecomp DECOMPOSITION FILE\n" +
      "Create a directed graph representation of all tasks and\n" +
      "decompositions reachable starting from TASK or DECOMPOSITION.\n" +
      "In this graph, each task and decomposition is a node, and\n" +
      "there is an edge from each task to its decomposition, and each\n" +
      "decomposition to its steps.\n" +
      "The graph is written in Graphviz 'dot' format.  See\n" +
      "http://www.graphviz.org for visualization tools which process\n" +
      "this format.")
  public void _dotdecomp(String arg) throws IOException, InterruptedException {
    String[] args = arg.trim().split("\\s+", 2);
    QName name = XMLUtil.parseQName(getModelSet(), args[0]);
    Dottable dot = null;
   
    TaskType task = getModelSet().getTaskType(name);
    if (task != null)
      dot = task.decompositionGraph();
    else {
      DecompositionType decomp = getModelSet().getDecompositionType(name);
      if (decomp != null)
        dot = decomp.decompositionGraph();
    }
   
    if (dot == null)
      out.println("Unknown ID: " + arg);
    else if (args.length < 2)
      DotUtils.showGraph(dot);
    else {
      PrintWriter w = new PrintWriter(args[1]);
      try {
       
        dot.printDot(w);
      } finally {
        w.close();
      }
    }
  }
 
  @Desc("create a bindings graph for a decomposition")
  @Help("Usage: dotbindings DECOMPOSITION FILE\n" +
      "Create a graph representation of the data flow from inputs\n" +
      "to outputs within a particular task decomposition.\n" +
      "The graph is written in Graphviz 'dot' format.  See\n" +
      "http://www.graphviz.org for visualization tools which process\n" +
      "this format.")
  public void _dotbindings(String arg) throws IOException, InterruptedException {
    String[] args = arg.trim().split("\\s+", 2);
    QName name = XMLUtil.parseQName(getModelSet(), args[0]);
    DecompositionType type = getModelSet().getDecompositionType(name);
   
    if (type == null)
      out.println("Unknown ID: " + arg);
    else if (args.length < 2)
      DotUtils.showGraph(type.bindingGraph());
    else {
      PrintWriter w = new PrintWriter(args[1]);
      try {
        type.bindingGraph().printDot(w);
      } finally {
        w.close();
      }
    }
  }

  @Desc("perform static analysis of task models")
  @Help("Usage: check\n" +
      "Check all currently loaded task models for some easily-\n" +
      "diagnosed errors, including:\n" +
      "- Task decompositions must refer to a known task.\n" +
      "- Tasks must have known decompositions, scripts, or dialogue.\n" +
      "- Decomposition steps must refer to known tasks.\n" +
      "- Decomposition bindings must refer to known inputs/outputs.\n" +
      "- Decompositions must bind all outputs of a task.")
  public void _check(String arg) {
    List<String> errors = new ArrayList<String>();
    getModelSet().checkAll(errors);
   
    for (String err : errors)
      out.println(err);
  }
 
  @Desc("set the random number generator seed")
  @Help("Usage: randseed SEED\n" +
      "Set the seed used for all psuedorandomness, including\n" +
      "choosing from multiple recipes and/or dialogue turns.\n" +
      "This is useful for creating reproducible test cases.")
  public void _randseed(String arg) {
    Priority.setRandSeed(Long.parseLong(arg));
  }
 
  public static void main(String[] args) throws Exception {
    AgentMain.consoleMain(args);
  }
}
TOP

Related Classes of edu.neu.ccs.task.agent.AgentConsole

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.