static L<GazelleTree> gazelle_getChildren(GazelleTree tree, O... _) {
  if (tree == null) null;
  if (tree.children != null) ret tree.children;
  
  double qualityCutOff = optPar qualityCutOff(_, -0.75);
  LS dialogHistory = optPar dialogHistory(_);
  
  // make rule engine etc.
  if (tree.ctx == null)
    tree.ctx = assertNotNull('evalContext, (GazelleEvalContext) callF(assertNotNull('makeEvalContext, optPar makeEvalContext(_))));
  GazelleEvalContext ctx = tree.ctx;
  
  new L<GazelleTree> children;
  
  SS inputMap = litmap(standard := tree.line);
  LS history = concatLists(dialogHistory, tree.history());
  for (int i = 0; i < l(history); i++)
    inputMap.put("dialog-" + (l(history)-i), history.get(i));

  L<RuleEngine2_MatchedRule> l = sortByMethodDesc qualitySum(ai_ruleEngine2_rulesForInput_5_allConditions(tree.ctx.engine, inputMap, debug := false));
  //print("Got rules: " + l(l));
  
  for (RuleEngine2_MatchedRule r : l) {
    if (r.relativeQuality() < qualityCutOff) continue;
    S line = r.outText();
    
    GazelleTree child = new(line);
    child.parent = tree;
    child.ctx = tree.ctx;
    child.mr = r;
    
    ai_gazelle_analyzeStatementsForRule_Data data = ctx.dataByRule.get(r.rule.globalID);
    child.prediction = data == null ? null : ai_gazelle_predictor1_onData(r, data);
    children.add(child);
  }
  sortByCalculatedFieldDesc(children, func(GazelleTree c) -> int { ai_goodBadToInt(c.prediction) });
  ret tree.children = children;
}