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

547
LINES

< > BotCompany Repo | #1030313 // Gazelle.rocks Multi-Bot [backup before adding Jython]

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

Uses 3098K of libraries. Click here for Pure Java version (21318L/122K).

1  
!7
2  
3  
set flag DynModule. // for transpilation
4  
5  
//set flag dm_evalJava_withModule_debug.
6  
//set flag veryQuickJava3_debug.
7  
8  
// store bot data per conversation
9  
concept Conversation {
10  
  S cookie;
11  
  S dataStruct;
12  
}
13  
14  
cmodule2 GazelleMultiBot > DynGazelleBot {
15  
  switchable int maxEvalResultLength = oneMegabyte_int();
16  
  switchable double evalTimeout = 30.0;
17  
  switchable long botProcessed; // timestamp of last post processed by bots
18  
  switchable int maxBotAnswerLength = 100000;
19  
  switchable bool runAutoBots = true;
20  
  switchable int maxRuntimeMinutes = 60;
21  
  transient new L<Bot> bots;
22  
  Map<Long, GazellePost> allPosts = syncTreeMap();
23  
  transient Map<Long, O> loadedCodePosts = syncTreeMap();
24  
  transient new O couldntLoadCode;
25  
26  
  transient Lock codeLoadLock = lock();
27  
28  
  set flag NoNanoHTTPD.
29  
  !include #1029545 // API for Eleu
30  
31  
  class Bot {
32  
    S name;
33  
    
34  
    *() {}
35  
    *(S *name) {}
36  
    *(S *name, IVF1<GazellePost> *handlePost) {}
37  
    
38  
    swappable void handlePost(GazellePost post) {}
39  
    
40  
    GazelleBotCred cred() { ret GazelleBotCred(_user, _botToken, name); }
41  
    
42  
    void postReply(GazellePost post, S text, S type, S title default null) {
43  
      if (empty(text) && empty(title))
44  
        text = "<no output>";
45  
      gazelle_createPost(cred(), text, type, refs := post.id, +title);
46  
    }
47  
    
48  
    void createPostFromBotResult(GazellePost post, IF0 f, IF1<S> modifyBotInfo default null) {
49  
      S text, type = "Code Result", title = "", botInfo = "";
50  
      try {
51  
        O result = f!;
52  
        if (result cast CreatePost) {
53  
          O[] params = toObjectArray(result.params);
54  
          text = (S) optPar text(params);
55  
          type = (S) optPar type(params, type);
56  
          title = (S) optPar title(params);
57  
          botInfo = (S) optPar botInfo(params); 
58  
        } else
59  
          text = str_shortenSyntheticAndStandardToString(result);
60  
      } catch print e {
61  
        text = getStackTrace(e);
62  
      }
63  
      if (empty(text) && empty(title))
64  
        text = "<no output>";
65  
      botInfo = callFOrKeep(modifyBotInfo, botInfo);
66  
      GazelleBotCred cred = cred();
67  
      if (nempty(botInfo))
68  
        cred.botInfo = botInfo;
69  
      gazelle_createPost(cred, text, type, +title, refs := post.id);
70  
    }
71  
  }
72  
73  
  srecord CreatePost(L params) {}
74  
  
75  
  start {
76  
    dm_useLocalMechListCopies();
77  
    dbIndexing(Conversation, "cookie");
78  
    
79  
    grabLoop.handlePosts = posts -> {
80  
      dm_mediumRefreshTranspiler();
81  
      
82  
      grabLoop.handlePosts_base(posts);
83  
      
84  
      for (GazellePost post : posts)
85  
        setField(botProcessed := max(botProcessed, post.modified));
86  
    };
87  
    
88  
    // legacy conversion to sort allPosts
89  
    setField_noCheck(allPosts := asSyncTreeMap(allPosts));
90  
91  
    bots.add(new Bot("Math Bot", post -> {
92  
      if (post.creating) ret;
93  
      gazelle_mathBot1_handlePost_2(_user, _botToken, post);
94  
    }));
95  
    
96  
    bots.add(new Bot("Code Safety Checker") {
97  
      void handlePost(GazellePost post) {
98  
        if (post.creating) ret;
99  
        if (post.isJavaXCode())
100  
          gazelle_createPost(cred(), codeSafetyCheckResult(post.text), "Code Safety", refs := post.id);
101  
      }
102  
    });
103  
    
104  
    bots.add(new Bot("Safe Code Runner") {
105  
      void handlePost(GazellePost post) {
106  
        if (post.creating) ret;
107  
        if (post.isJavaXCode()) {
108  
          int runtime = min(maxRuntimeMinutes, parseFirstInt(jextractIC("runtime <int> minutes", post.type)));
109  
          S code = post.text;
110  
          if (isCodeSafe(code)) {
111  
            S _code = prepareCode(code, post);
112  
            //S out = shorten(maxEvalResultLength, runCode(code));
113  
            createPostFromBotResult(post, () -> evalCode(runtime*60.0, _code, post));
114  
          }
115  
        }
116  
      }
117  
    });
118  
    
119  
    bots.add(new Bot("Run Code On All Posts") {
120  
      void handlePost(GazellePost post) {
121  
        if (post.creating) ret;
122  
        if (eqic(post.type, "Instruction") && match("Please run this code on all posts", post.text)) {
123  
          long ref = gazelle_firstPostRef(post.id);
124  
          if (ref == 0) ret;
125  
          
126  
          S code = gazelle_text(ref);
127  
          if (!isCodeSafe(code)) ret;
128  
          
129  
          S code2 = "ret func(S post) { " + code + " };";
130  
          O function = evalCode(code2);
131  
          
132  
          new LS lines;
133  
          S out = shorten(maxEvalResultLength, runCode(code));
134  
          for (GazellePost post2 : cloneValues(allPosts)) {
135  
            lines.add("Post " + post2.id + " (" + quote(shorten(20, post2.text)) + "): " + shorten(80, runFunc(() -> callF(function, post2.text))));
136  
          }
137  
138  
          gazelle_createPost(cred(), lines(lines), "Code Result", refs := post.id);
139  
        }
140  
      }
141  
    });
142  
    
143  
    bots.add(new Bot("Mark identifiers safe") {
144  
      void handlePost(GazellePost post) {
145  
        if (post.creating) ret;
146  
        if (/*eqic(post.type, "Instruction")
147  
          &&*/ post.creator.isMaster
148  
          && match("Mark safe", post.text)) {
149  
          S text = getPost(first(post.postRefs)).text;
150  
          LS ids = tok_identifiersInOrder(regexpFirstGroupIC("Unknown identifiers: (.+)", text));
151  
          print("Marking safe: " + ids);
152  
          postReply(post, markSafe(ids), "Marked safe");
153  
        }
154  
      }
155  
    });
156  
    
157  
    bots.add(new Bot("Post Deleter") {
158  
      void handlePost(GazellePost post) {
159  
        if (post.creating) ret;
160  
        if (/*eqic(post.type, "Instruction")
161  
          &&*/ post.creator.isMaster
162  
          && match("Delete posts", post.text)) {
163  
            long ref = gazelle_firstPostRef(post.id);
164  
            if (ref == 0) ret;
165  
            S text = gazelle_text(ref);
166  
            L<Long> postIDs = allToLong(regexpAllFirstGroups(gazelle_deletePostRegexp(), text));
167  
            print("Deleting posts: " + postIDs);
168  
            if (nempty(postIDs)) {
169  
              Map result = gazelle_deletePosts(cred(), postIDs);
170  
              postReply(post, str(result), "Deletion result");
171  
            } else
172  
              postReply(post, "No mentioned posts found", "Deletion result");
173  
        }
174  
      }
175  
    });
176  
    
177  
    bots.add(new Bot("Detector Runner") {
178  
      void handlePost(GazellePost post) {
179  
        if (post.creating) ret;
180  
        ret unless eqic(post.type, "Instruction")
181  
          && match("Please run detector", post.text);
182  
          
183  
        try {
184  
          long detectorID = post.refWithTagOrFail("detector");
185  
          long posExamplesID = post.refWithTagOrFail("positive examples");
186  
          long negExamplesID = post.refWithTagOrFail("negative examples");
187  
        
188  
          S code = getPost(detectorID).text;
189  
          LS posExamples = tlft(getPost(posExamplesID).text);
190  
          LS negExamples = tlft(getPost(negExamplesID).text);
191  
          LPair<S, Bool> examples = trueFalseBPairs(posExamples, negExamples);
192  
          if (!isCodeSafe(code)) fail("Detector code not safe");
193  
  
194  
          IF1<S, O> detector = proxy IF1(evalCode(code));
195  
          new LS good;
196  
          new LS bad;
197  
          new Scorer scorer;
198  
          long time = sysNow();
199  
          evalWithTimeoutOrFail(evalTimeout, r {
200  
            for (Pair<S, Bool> example : examples) {
201  
              S result = runFunc(() -> detector.get(example.a));
202  
              bool ok = eq(result, str(example.b));
203  
              scorer.add(ok);
204  
              S line = (ok ? "OK" : example.b ? "False negative" : "False positive")
205  
                  + " (" + shorten(10, result) + "): " + example.a;
206  
              (ok ? good : bad).add(line);
207  
            }
208  
          });            
209  
          time = sysNow()-time;
210  
211  
          S text = "Detector code:\n\n" + indentx(code) + "\n\n"
212  
            + n2(good, "correct answer") + ", " + n2(bad, "bad answer") + ". Runtime: " + n2(time) + " ms\n\n"
213  
            + or2(trim(lines(concatLists(bad, ll(""), good))), "No errors");
214  
          S title = "Score for detector " + detectorID + ": " + scorer;
215  
          
216  
          gazelle_createPost(cred(), text, "Detector Score", +title,
217  
            refs := joinWithSpace(ll(post.id, detectorID, posExamplesID, negExamplesID)),
218  
            refTags := linesLL_rtrim("", "detector", "positive examples", "negative examples"));
219  
        } catch e {
220  
          postReply(post, getStackTrace(e), "Error");
221  
        }
222  
      }
223  
    });
224  
    
225  
    bots.add(new Bot("Auto Bot Runner") {
226  
      void handlePost(GazellePost post) {
227  
        if (!runAutoBots) ret;
228  
        if (post.creating) ret;
229  
        if (post.isAutoBotMade()) ret;
230  
231  
        print("Auto Bot Runner: " + post.id);
232  
        for (GazellePost botPost : values(allPosts)) {
233  
          continue unless eqic(botPost.type, "JavaX Code (Bot run on every post)")
234  
            && botPost.creator.isMaster;
235  
236  
          print("Processing auto-bot " + botPost.id + " on " + post.id);
237  
          
238  
          O code = codeForPost(botPost);
239  
          if (code == couldntLoadCode)
240  
            continue with print("Couldn't load code for auto-bot " + botPost.id);
241  
          print("Code type: " + className(code));
242  
          O botInstance = evalWithTimeoutOrFail(evalTimeout, () -> callFOrNewInstance(code));
243  
          print("Bot instance type: " + className(botInstance));
244  
245  
          setOpt(botInstance, post := post.text);
246  
          
247  
          createPostFromBotResult(post, () -> call(botInstance, "calc"),
248  
            botInfo -> joinNemptiesWithColon("Auto-Bot " + botPost.id, botInfo));
249  
        }
250  
      }
251  
    });
252  
253  
    dm_doEvery(60.0, 3600.0, r removeDeletedPosts);
254  
  }
255  
  
256  
  void handlePost(GazellePost post) {
257  
    allPosts.put(post.id, post);
258  
    change();
259  
    loadedCodePosts.remove(post.id);
260  
    if (post.modified > botProcessed) {
261  
      print("modified: " + post.modified + "/" + botProcessed);
262  
      for (Bot bot : bots) pcall {
263  
        bot.handlePost(post);
264  
      }
265  
    }
266  
  }
267  
268  
  // if post != null, store transpilation
269  
  O evalCode(S code, GazellePost post default null) {
270  
    ret evalCode(evalTimeout, code, post);
271  
  }
272  
  
273  
  O evalCode(double timeout, S code, GazellePost post default null) {
274  
    printWithIndent("CODE> ", code);
275  
    veryQuickJava_transpiled.set(post != null ? "" : null); // request transpilation
276  
    try {
277  
      ret dm_javaEvalWithTimeout(timeout, code);
278  
    } finally {
279  
      if (post != null) {
280  
        S java = veryQuickJava_transpiled!;
281  
        print("Transpilation for " + post.id + ": " + shorten(java));
282  
        saveTextFile(transpilationFile(post.id), nullOnEmpty(java));
283  
      }
284  
    }
285  
  }
286  
287  
  // assumes code is safety-checked
288  
  S runCode(S code) {
289  
    printWithIndent("CODE> ", code);
290  
    ret runFunc(() -> str_shortenSyntheticAndStandardToString(dm_javaEval(code)));
291  
  }
292  
293  
  // run IF0 with timeout, exception to string, convert result to string
294  
  S runFunc(IF0 f) {
295  
    ret str_shortenSyntheticAndStandardToString(evalWithTimeoutOrException(evalTimeout, func {
296  
      try {
297  
        ret str_shortenSyntheticAndStandardToString(f!);
298  
      } catch e {
299  
        ret getStackTrace(e);
300  
      }
301  
    }));
302  
  }
303  
  
304  
  L<GazellePost> repliesTo(GazellePost post) {
305  
    ret filter(values(allPosts), p -> contains(p.postRefs, post.id));
306  
  }
307  
  
308  
  L<GazellePost> repliesWithTag(GazellePost post, S tag) {
309  
    Pair<Long, S> pair = pair(post.id, upper(tag));
310  
    ret filter(repliesTo(post), p -> contains(mapPairsB toUpper(p.taggedRefs()), pair));
311  
  }
312  
  
313  
  GazellePost getPost(long id) {
314  
    ret allPosts.get(id);
315  
  }
316  
  
317  
  S getPostText(long id) {
318  
    ret getPost(id).text;
319  
  }
320  
  
321  
  Cl<GazellePost> getAllPosts() {
322  
    ret values(allPosts);
323  
  }
324  
325  
  CreatePost createPost(O... _) {
326  
    ret new CreatePost(asList(_));
327  
  }
328  
  
329  
  CodeSafetyChecker codeSafetyChecker() {
330  
    new CodeSafetyChecker checker;
331  
    checker.init();
332  
    checker.markSafe("getAllPosts");
333  
    ret checker;
334  
  }
335  
  
336  
  S codeSafetyCheckResult(S code) {
337  
    CodeSafetyChecker checker = codeSafetyChecker();
338  
    checker.checkCode(code);
339  
    ret checker.verbalCheckResult();
340  
  }
341  
  
342  
  bool isCodeSafe(S code) {
343  
    CodeSafetyChecker checker = codeSafetyChecker();
344  
    checker.checkCode(code);
345  
    ret checker.isSafe();
346  
  }
347  
348  
  S prepareCode(S code, GazellePost post) {
349  
    if (tok_isStaticLevelCode(code)) ret code;
350  
    
351  
    code = tok_addReturn(code);
352  
    
353  
    // define implicit vars and functions
354  
355  
    // post = text of parent post
356  
    if (containsJavaToken(code, "post")) {
357  
      long ref = gazelle_firstPostRef(post.id);
358  
      if (ref != 0)
359  
        code = "S post = " + quote(gazelle_text(ref)) + ";\n" + code;
360  
    }
361  
    
362  
    // post = text of grandparent post
363  
    if (containsJavaToken(code, "post2")) {
364  
      long ref = gazelle_firstPostRef(gazelle_firstPostRef(post.id));
365  
      if (ref != 0)
366  
        code = "S post2 = " + quote(gazelle_text(ref)) + ";\n" + code;
367  
    }
368  
    
369  
    if (containsJavaToken(code, "getAllPosts"))
370  
      code = [[
371  
        embedded Cl<GazellePost> getAllPosts() {
372  
          ret lazyMap_bitSet quickImport(asList((Cl) dm_call(dm_current_generic(), "getAllPosts")));
373  
        }
374  
      ]] + code;
375  
376  
    if (containsJavaToken(code, "createPost"))
377  
      code = [[
378  
        embedded O createPost(O... _) {
379  
          ret dm_call(dm_current_generic(), "createPost", _);
380  
        }
381  
      ]] + code;
382  
383  
    // optimize gazelle_text
384  
    if (containsJavaToken(code, "gazelle_text")) {
385  
      code = [[
386  
        embedded S gazelle_text(long id) {
387  
          ret (S) dm_call(dm_current_generic(), "getPostText", id);
388  
        }
389  
      ]] + code;
390  
    }
391  
    
392  
    ret code;
393  
  }
394  
395  
  O html(IWebRequest req) {
396  
    new Matches m;
397  
    
398  
    if (eqic(req.uri(), "/favicon.ico"))
399  
      ret serveFile(loadLibrary(#1400439), faviconMimeType());
400  
401  
    if (startsWith(req.uri(), "/htmlBot/", m) && isInteger(m.rest())) {
402  
      req.noSpam();
403  
      long postID = parseLong(m.rest());
404  
      GazellePost post = getPost(postID);
405  
      if (post == null) ret serve404("Post " + postID + " not found");
406  
      O code = codeForPost(post);
407  
      IF0 calc;
408  
409  
      // Case 1: Static page (code just returns a string)
410  
      if (code instanceof S)
411  
        calc = () -> code;
412  
        
413  
      // Case 2: Code is an argumentless function
414  
      else if (implementsInterfaceShortNamed IF0(code))
415  
        calc = toIF0(code);
416  
417  
      else {
418  
        // Case 3: Code is IF1<IWebRequest, S>
419  
        // Sadly, for a lambda like (IF1<IWebRequest, S>) req -> ..., we can't find the
420  
        // IWebRequest type by reflection. So we find the IWebRequest interface by name.
421  
        Class reqType = getClassInRealm("main$IWebRequest", code);
422  
        print(+reqType);
423  
        if (reqType == null) fail("IWebRequest not found in bot");
424  
        O wrappedReq = proxy(reqType, req);
425  
        calc = () -> callF(code, wrappedReq);
426  
      }
427  
      
428  
      O result = evalWithTimeoutOrFail(evalTimeout, () -> calc!);
429  
      ret (S) result;
430  
    }
431  
    
432  
    if (startsWith(req.uri(), "/chatBotReply/", m) && isInteger(m.rest())) {
433  
      req.noSpam();
434  
      long postID = parseLong(m.rest());
435  
      GazellePost post = getPost(postID);
436  
      if (post == null) ret serve404("Post " + postID + " not found");
437  
      O code = codeForPost(post);
438  
      if (code == couldntLoadCode) ret withHeader(serve500("Couldn't load code for post"));
439  
      S q = req.params().get("q"); // user input
440  
      bool initial = eq(req.params().get("initial"), "1");
441  
      S cookie = req.params().get("cookie");
442  
      if (!initial && empty(q)) ret withHeader(serveText(""));
443  
444  
      S answer = "";
445  
      if (implementsInterfaceShortNamed IF1(code)) { // stateless bot
446  
        if (!initial)
447  
          answer = evalWithTimeoutOrFail(evalTimeout, () -> strOrEmpty(callF(code, q)));
448  
      } else {
449  
        print("Stateful bot. cookie: " + cookie);
450  
        if (empty(cookie)) ret withHeader(serve500("Need cookie for stateful bot"));
451  
        Conversation conv = uniq(Conversation, +cookie);
452  
        O instance = code;
453  
        if (instance == null) ret withHeader(serve500("No bot instance"));
454  
        print("Stateful bot instance: " + instance);
455  
        if (nempty(conv.dataStruct)) {
456  
          O data = unstructureInRealm(conv.dataStruct, instance); // hopefully this is safe
457  
          print("Unstructured: " + data);
458  
          copyAllThisDollarFields(instance, data); // probably not needed anymore
459  
          instance = data;
460  
        }
461  
        O _instance = instance;
462  
        answer = evalWithTimeoutOrFail(evalTimeout, () -> strOrEmpty(
463  
          initial ? callOpt(_instance, "initialMessage") : call(_instance, "answer", q)));
464  
        cset(conv, dataStruct := structure(instance));
465  
        print("Structured: " + conv.dataStruct);
466  
      }
467  
468  
      if (initial && empty(answer))
469  
        answer = "Bot " + post.id + " ready";
470  
471  
      ret withHeader(serveJSON(litorderedmap(answer := shorten(maxBotAnswerLength, answer))));
472  
    }
473  
    
474  
    if (startsWith(req.uri(), "/transpilation/", m) && isInteger(m.rest())) {
475  
      long postID = parseLong(m.rest());
476  
      GazellePost post = getPost(postID);
477  
      if (post == null) ret serve404("Post " + postID + " not found");
478  
      S src = loadTextFile(transpilationFile(post.id));
479  
      if (empty(src))
480  
        src = "No transpilation found for post " + postID + "." + 
481  
          (!post.isJavaXCode() ? " Code is not a code post." : " Please try \"Touch post\" and wait a few seconds");
482  
      else pcall {
483  
        src = javaPrettyPrint(src);
484  
      }
485  
      ret serveText(src);
486  
    }
487  
488  
    if (eq(req.uri(), "/")) {
489  
      ret "Loaded code posts:"
490  
        + ul(keys(loadedCodePosts));
491  
    }
492  
    
493  
    ret serve404();
494  
  }
495  
496  
  O codeForPost(GazellePost post) {
497  
    lock codeLoadLock;
498  
    O code = loadedCodePosts.get(post.id);
499  
    if (code == null) {
500  
      try {
501  
        S codeText = post.text;
502  
        if (!isCodeSafe(codeText)) fail("Code is not safe: " + codeSafetyCheckResult(codeText));
503  
        codeText = prepareCode(codeText, null);
504  
        code = evalCode(codeText, post);
505  
      } catch print e {
506  
        code = couldntLoadCode;
507  
      }
508  
      loadedCodePosts.put(post.id, code);
509  
    }
510  
    ret code;
511  
  }
512  
513  
  File transpilationFile(long postID) {
514  
    ret programFile("Post-Transpilations/" + postID + ".java");
515  
  }
516  
517  
  O withHeader(O response) {
518  
    call(response, 'addHeader, "Access-Control-Allow-Origin", "*");
519  
    ret response;
520  
  }
521  
522  
  O _getReloadData() {
523  
    ret loadedCodePosts;
524  
  }
525  
  
526  
  void _setReloadData(Map<Long, O> data) {
527  
    if (data != null)
528  
      loadedCodePosts = data;
529  
  }
530  
531  
  void forgetLoadedCodePosts { clear(loadedCodePosts); }
532  
533  
  void enhanceFrame(Container f) {
534  
    super.enhanceFrame(f);
535  
    internalFramePopupMenuItems(f,
536  
      "Forget loaded code", rEnter forgetLoadedCodePosts,
537  
      "Remove deleted posts", rThreadEnter removeDeletedPosts);
538  
  }
539  
540  
  void removeDeletedPosts {
541  
    Cl<Long> posts = asSet(grabLoop.allPostIDs());
542  
    print("Keeping " + nPosts(posts));
543  
    if (syncRemoveAllExcept(allPosts, posts)) change();
544  
    print("New count: " + nPosts(allPosts));
545  
    removeAllExcept(loadedCodePosts, posts);
546  
  }
547  
}

Author comment

Began life as a copy of #1029997

download  show line numbers  debug dex  old transpilations   

Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt

No comments. add comment

Snippet ID: #1030313
Snippet name: Gazelle.rocks Multi-Bot [backup before adding Jython]
Eternal ID of this version: #1030313/1
Text MD5: 1f843b79b4666eda2b9e5739d32f24a3
Transpilation MD5: 3061bfc6bace1d6f2fb64f3e7f279510
Author: stefan
Category: javax / gazelle.rocks
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-11-30 15:09:38
Source code size: 19116 bytes / 547 lines
Pitched / IR pitched: No / No
Views / Downloads: 191 / 254
Referenced in: [show references]