Skip to content

Commit

Permalink
feat(shell): dynamic help from agents using __doc__ parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
mchitre committed Nov 29, 2024
1 parent 81d777a commit 4484ee0
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 34 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.14.0
1.15.0
6 changes: 4 additions & 2 deletions src/main/groovy/org/arl/fjage/shell/BaseGroovyScript.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,8 @@ abstract class BaseGroovyScript extends Script {
Binding binding = getBinding()
if (binding.hasVariable('__doc__')) {
def doc = binding.getVariable('__doc__')
return doc.get(obj as String)
Agent a = binding.hasVariable('__agent__') ? binding.getVariable('__agent__') : null
return doc.get(a, obj as String)
}
return null
}
Expand All @@ -509,7 +510,8 @@ abstract class BaseGroovyScript extends Script {
Binding binding = getBinding()
if (binding.hasVariable('__doc__')) {
def doc = binding.getVariable('__doc__')
return doc.get()
Agent a = binding.hasVariable('__agent__') ? binding.getVariable('__agent__') : null
return doc.get(a)
}
return null
}
Expand Down
116 changes: 85 additions & 31 deletions src/main/java/org/arl/fjage/shell/Documentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,81 +13,108 @@
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.arl.fjage.*;
import org.arl.fjage.param.*;

public class Documentation {

protected List<String> doc = new ArrayList<String>();
protected Map<String,Integer> ndx = new HashMap<String,Integer>();
protected Pattern heading = Pattern.compile("^#+ +([^ ]+) +-.*$");
protected Pattern section = Pattern.compile("^#+ .*$");
protected int staticSize = 0;

protected static final Pattern heading = Pattern.compile("^#+ +([^ ]+) +-.*$");
protected static final Pattern section = Pattern.compile("^#+ .*$");
protected static final String crlf = "\\r?\\n";
protected static final String header = "^#+ +";
protected static final String gaps = "\\n+$";
protected static final String placeholder = "@@"; // to be replaced by agent name

/**
* Add markdown documentation.
*
* @param s multiline string.
*/
public void add(String s) {
String[] lines = s.split("\\r?\\n");
for (String line: lines) {
Matcher m = heading.matcher(line);
if (m.matches()) ndx.put(m.group(1), doc.size());
if (doc.size() > staticSize) doc.subList(staticSize, doc.size()).clear();
String[] lines = s.split(crlf);
for (String line: lines)
doc.add(line);
}
staticSize = doc.size();
}

/**
* Get documentation index.
*/
public String get() {
public String get(Agent agent) {
build(agent);
List<String> topics = new ArrayList<String>();
for (String s: doc)
if (s.startsWith("# ")) topics.add(s.substring(2));
topics.sort(null);
StringBuilder sb = new StringBuilder();
for (Integer v: ndx.values()) {
String s = doc.get(v);
if (s.startsWith("# ")) {
sb.append(s.substring(2));
sb.append('\n');
}
for (String s: topics) {
sb.append(s);
sb.append('\n');
}
return sb.toString();
}

/**
* Get documentation by keyword.
*
* @param keyword keyword.
*/
public String get(Agent agent, String keyword) {
build(agent);
int count = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < doc.size(); i++) {
String s = doc.get(i);
Matcher m = heading.matcher(s);
if (m.matches() && m.group(1).equals(keyword)) {
extract(sb, i);
count++;
}
}
if (count == 0) search(sb, keyword);
return sb.toString().replaceAll(gaps, "\n");
}

/**
* Search documentation topics by keyword.
*
* @param sb string builder.
* @param keyword keyword.
*/
public String search(String keyword) {
protected void search(StringBuilder sb, String keyword) {
keyword = keyword.toLowerCase();
Set<String> topics = new HashSet<String>();
String topic = null;
for (String s: doc) {
Matcher m = section.matcher(s);
if (m.matches()) topic = s.replaceAll("^#+ +", "- ");
if (m.matches()) topic = s.replaceAll(header, "- ");
if (s.toLowerCase().contains(keyword)) topics.add(topic);
}
if (topics.size() == 0) return null;
StringBuilder sb = new StringBuilder();
if (topics.size() == 0) return;
sb.append("Possible topics:\n");
for (String s: topics) {
sb.append(s);
sb.append('\n');
}
return sb.toString();
}

/**
* Get documentation by keyword.
* Extract documentation.
*
* @param keyword keyword.
* @param sb string builder.
* @param pos position.
*/
public String get(String keyword) {
Integer pos = ndx.get(keyword);
if (pos == null) return search(keyword);
protected void extract(StringBuilder sb, int pos) {
String s = doc.get(pos);
int level = s.indexOf(' ');
if (level <= 0) return null;
if (level <= 0) return;
int endlevel = level;
StringBuilder sb = new StringBuilder();
sb.append(s.replaceAll("^#+ +", ""));
if (s.startsWith("# ")) sb.append(s);
else sb.append(s.replaceAll(header, ""));
sb.append('\n');
int skip = 0;
for (int i = pos+1; i < doc.size(); i++) {
Expand All @@ -101,11 +128,11 @@ public String get(String keyword) {
if (skip == 0 || level <= skip) {
if (skip > 0) nl = true;
skip = 0;
s = s.replaceAll("^#+ +", "");
s = s.replaceAll(header, "");
}
if (m.matches()) {
skip = level;
sb.append("- ");
sb.append("* ");
sb.append(s);
sb.append('\n');
} else if (nl) {
Expand All @@ -117,7 +144,34 @@ public String get(String keyword) {
sb.append('\n');
}
}
return sb.toString().replaceAll("\\n+$", "\n");
sb.append('\n');
}

/**
* Build dynamic documentation by querying agents.
*
* @param agent agent.
*/
protected void build(Agent agent) {
if (agent == null) return;
if (doc.size() > staticSize) doc.subList(staticSize, doc.size()).clear();
Container c = agent.getContainer();
AgentID[] agentIDs = c.getAgents();
for (AgentID a: agentIDs) {
ParameterReq req = new ParameterReq();
req.setRecipient(a);
req.get(new NamedParameter("__doc__"));
Message rsp = agent.request(req, 1000);
if (rsp != null && rsp instanceof ParameterRsp) {
Object docstr = ((ParameterRsp)rsp).get(new NamedParameter("__doc__"));
if (docstr != null) {
String s = docstr.toString().replaceAll(placeholder, a.getName());
String[] lines = s.split(crlf);
for (String line: lines)
doc.add(line);
}
}
}
}

}

0 comments on commit 4484ee0

Please sign in to comment.