Uses 3098K of libraries. Click here for Pure Java version (20601L/117K).
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 | transient new L<Bot> bots; |
20 | Map<Long, GazellePost> allPosts = syncTreeMap(); |
21 | transient Map<Long, O> loadedCodePosts = syncTreeMap(); |
22 | transient new O couldntLoadCode; |
23 | |
24 | transient Lock codeLoadLock = lock(); |
25 | |
26 | set flag NoNanoHTTPD. |
27 | !include #1029545 // API for Eleu |
28 | |
29 | class Bot {
|
30 | S name; |
31 | |
32 | *() {}
|
33 | *(S *name) {}
|
34 | *(S *name, IVF1<GazellePost> *handlePost) {}
|
35 | |
36 | swappable void handlePost(GazellePost post) {}
|
37 | |
38 | GazelleBotCred cred() { ret GazelleBotCred(_user, _botToken, name); }
|
39 | |
40 | void postReply(GazellePost post, S text, S type, S title default null) {
|
41 | if (empty(text) && empty(title)) |
42 | text = "<no output>"; |
43 | gazelle_createPost(cred(), text, type, refs := post.id, +title); |
44 | } |
45 | } |
46 | |
47 | srecord CreatePost(L params) {}
|
48 | |
49 | start {
|
50 | dm_useLocalMechListCopies(); |
51 | dbIndexing(Conversation, "cookie"); |
52 | |
53 | grabLoop.handlePosts = posts -> {
|
54 | dm_mediumRefreshTranspiler(); |
55 | |
56 | grabLoop.handlePosts_base(posts); |
57 | |
58 | for (GazellePost post : posts) |
59 | setField(botProcessed := max(botProcessed, post.modified)); |
60 | }; |
61 | |
62 | // legacy conversion to sort allPosts |
63 | setField_noCheck(allPosts := asSyncTreeMap(allPosts)); |
64 | |
65 | bots.add(new Bot("Math Bot", post -> {
|
66 | if (post.creating) ret; |
67 | gazelle_mathBot1_handlePost_2(_user, _botToken, post); |
68 | })); |
69 | |
70 | bots.add(new Bot("Code Safety Checker") {
|
71 | void handlePost(GazellePost post) {
|
72 | if (post.creating) ret; |
73 | if (post.isJavaXCode()) |
74 | gazelle_createPost(cred(), codeSafetyCheckResult(post.text), "Code Safety", refs := post.id); |
75 | } |
76 | }); |
77 | |
78 | bots.add(new Bot("Safe Code Runner") {
|
79 | void handlePost(GazellePost post) {
|
80 | if (post.creating) ret; |
81 | if (post.isJavaXCode()) {
|
82 | S code = post.text; |
83 | if (isCodeSafe(code)) {
|
84 | code = prepareCode(code, post); |
85 | //S out = shorten(maxEvalResultLength, runCode(code)); |
86 | S text, type = "Code Result", title = ""; |
87 | try {
|
88 | O result = evalCode(code, post); |
89 | if (result cast CreatePost) {
|
90 | O[] params = toObjectArray(result.params); |
91 | text = (S) optPar text(params); |
92 | type = (S) optPar type(params, type); |
93 | title = (S) optPar title(params); |
94 | } else |
95 | text = str_shortenSyntheticAndStandardToString(result); |
96 | } catch print e {
|
97 | text = getStackTrace(e); |
98 | } |
99 | if (empty(text) && empty(title)) |
100 | text = "<no output>"; |
101 | gazelle_createPost(cred(), text, type, +title, refs := post.id); |
102 | } |
103 | } |
104 | } |
105 | }); |
106 | |
107 | bots.add(new Bot("Run Code On All Posts") {
|
108 | void handlePost(GazellePost post) {
|
109 | if (post.creating) ret; |
110 | if (eqic(post.type, "Instruction") && match("Please run this code on all posts", post.text)) {
|
111 | long ref = gazelle_firstPostRef(post.id); |
112 | if (ref == 0) ret; |
113 | |
114 | S code = gazelle_text(ref); |
115 | if (!isCodeSafe(code)) ret; |
116 | |
117 | S code2 = "ret func(S post) { " + code + " };";
|
118 | O function = evalCode(code2); |
119 | |
120 | new LS lines; |
121 | S out = shorten(maxEvalResultLength, runCode(code)); |
122 | for (GazellePost post2 : cloneValues(allPosts)) {
|
123 | lines.add("Post " + post2.id + " (" + quote(shorten(20, post2.text)) + "): " + shorten(80, runFunc(() -> callF(function, post2.text))));
|
124 | } |
125 | |
126 | gazelle_createPost(cred(), lines(lines), "Code Result", refs := post.id); |
127 | } |
128 | } |
129 | }); |
130 | |
131 | bots.add(new Bot("Mark identifiers safe") {
|
132 | void handlePost(GazellePost post) {
|
133 | if (post.creating) ret; |
134 | if (/*eqic(post.type, "Instruction") |
135 | &&*/ post.creator.isMaster |
136 | && match("Mark safe", post.text)) {
|
137 | S text = getPost(first(post.postRefs)).text; |
138 | LS ids = tok_identifiersInOrder(regexpFirstGroupIC("Unknown identifiers: (.+)", text));
|
139 | print("Marking safe: " + ids);
|
140 | postReply(post, markSafe(ids), "Marked safe"); |
141 | } |
142 | } |
143 | }); |
144 | |
145 | bots.add(new Bot("Post Deleter") {
|
146 | void handlePost(GazellePost post) {
|
147 | if (post.creating) ret; |
148 | if (/*eqic(post.type, "Instruction") |
149 | &&*/ post.creator.isMaster |
150 | && match("Delete posts", post.text)) {
|
151 | long ref = gazelle_firstPostRef(post.id); |
152 | if (ref == 0) ret; |
153 | S text = gazelle_text(ref); |
154 | L<Long> postIDs = allToLong(regexpAllFirstGroups(gazelle_deletePostRegexp(), text)); |
155 | print("Deleting posts: " + postIDs);
|
156 | if (nempty(postIDs)) {
|
157 | Map result = gazelle_deletePosts(cred(), postIDs); |
158 | postReply(post, str(result), "Deletion result"); |
159 | } else |
160 | postReply(post, "No mentioned posts found", "Deletion result"); |
161 | } |
162 | } |
163 | }); |
164 | |
165 | bots.add(new Bot("Detector Runner") {
|
166 | void handlePost(GazellePost post) {
|
167 | if (post.creating) ret; |
168 | ret unless eqic(post.type, "Instruction") |
169 | && match("Please run detector", post.text);
|
170 | |
171 | try {
|
172 | long detectorID = post.refWithTagOrFail("detector");
|
173 | long posExamplesID = post.refWithTagOrFail("positive examples");
|
174 | long negExamplesID = post.refWithTagOrFail("negative examples");
|
175 | |
176 | S code = getPost(detectorID).text; |
177 | LS posExamples = tlft(getPost(posExamplesID).text); |
178 | LS negExamples = tlft(getPost(negExamplesID).text); |
179 | LPair<S, Bool> examples = trueFalseBPairs(posExamples, negExamples); |
180 | if (!isCodeSafe(code)) fail("Detector code not safe");
|
181 | |
182 | IF1<S, O> detector = proxy IF1(evalCode(code)); |
183 | new LS good; |
184 | new LS bad; |
185 | new Scorer scorer; |
186 | long time = sysNow(); |
187 | evalWithTimeoutOrFail(evalTimeout, r {
|
188 | for (Pair<S, Bool> example : examples) {
|
189 | S result = runFunc(() -> detector.get(example.a)); |
190 | bool ok = eq(result, str(example.b)); |
191 | scorer.add(ok); |
192 | S line = (ok ? "OK" : example.b ? "False negative" : "False positive") |
193 | + " (" + shorten(10, result) + "): " + example.a;
|
194 | (ok ? good : bad).add(line); |
195 | } |
196 | }); |
197 | time = sysNow()-time; |
198 | |
199 | S text = "Detector code:\n\n" + indentx(code) + "\n\n" |
200 | + n2(good, "correct answer") + ", " + n2(bad, "bad answer") + ". Runtime: " + n2(time) + " ms\n\n" |
201 | + or2(trim(lines(concatLists(bad, ll(""), good))), "No errors");
|
202 | S title = "Score for detector " + detectorID + ": " + scorer; |
203 | |
204 | gazelle_createPost(cred(), text, "Detector Score", +title, |
205 | refs := joinWithSpace(ll(post.id, detectorID, posExamplesID, negExamplesID)), |
206 | refTags := linesLL_rtrim("", "detector", "positive examples", "negative examples"));
|
207 | } catch e {
|
208 | postReply(post, getStackTrace(e), "Error"); |
209 | } |
210 | } |
211 | }); |
212 | } |
213 | |
214 | void handlePost(GazellePost post) {
|
215 | allPosts.put(post.id, post); |
216 | change(); |
217 | loadedCodePosts.remove(post.id); |
218 | if (post.modified > botProcessed) {
|
219 | print("modified: " + post.modified + "/" + botProcessed);
|
220 | for (Bot bot : bots) pcall {
|
221 | bot.handlePost(post); |
222 | } |
223 | } |
224 | } |
225 | |
226 | // if post != null, store transpilation |
227 | O evalCode(S code, GazellePost post default null) {
|
228 | printWithIndent("CODE> ", code);
|
229 | veryQuickJava_transpiled.set(post != null ? "" : null); // request transpilation |
230 | try {
|
231 | ret dm_javaEvalWithTimeout(evalTimeout, code); |
232 | } finally {
|
233 | if (post != null) {
|
234 | S java = veryQuickJava_transpiled!; |
235 | print("Transpilation for " + post.id + ": " + shorten(java));
|
236 | saveTextFile(transpilationFile(post.id), nullOnEmpty(java)); |
237 | } |
238 | } |
239 | } |
240 | |
241 | // assumes code is safety-checked |
242 | S runCode(S code) {
|
243 | printWithIndent("CODE> ", code);
|
244 | ret runFunc(() -> str_shortenSyntheticAndStandardToString(dm_javaEval(code))); |
245 | } |
246 | |
247 | // run IF0 with timeout, exception to string, convert result to string |
248 | S runFunc(IF0 f) {
|
249 | ret str_shortenSyntheticAndStandardToString(evalWithTimeoutOrException(evalTimeout, func {
|
250 | try {
|
251 | ret str_shortenSyntheticAndStandardToString(f!); |
252 | } catch e {
|
253 | ret getStackTrace(e); |
254 | } |
255 | })); |
256 | } |
257 | |
258 | L<GazellePost> repliesTo(GazellePost post) {
|
259 | ret filter(values(allPosts), p -> contains(p.postRefs, post.id)); |
260 | } |
261 | |
262 | L<GazellePost> repliesWithTag(GazellePost post, S tag) {
|
263 | Pair<Long, S> pair = pair(post.id, upper(tag)); |
264 | ret filter(repliesTo(post), p -> contains(mapPairsB toUpper(p.taggedRefs()), pair)); |
265 | } |
266 | |
267 | GazellePost getPost(long id) {
|
268 | ret allPosts.get(id); |
269 | } |
270 | |
271 | S getPostText(long id) {
|
272 | ret getPost(id).text; |
273 | } |
274 | |
275 | Cl<GazellePost> getAllPosts() {
|
276 | ret values(allPosts); |
277 | } |
278 | |
279 | CreatePost createPost(O... _) {
|
280 | ret new CreatePost(asList(_)); |
281 | } |
282 | |
283 | CodeSafetyChecker codeSafetyChecker() {
|
284 | new CodeSafetyChecker checker; |
285 | checker.init(); |
286 | checker.markSafe("getAllPosts");
|
287 | ret checker; |
288 | } |
289 | |
290 | S codeSafetyCheckResult(S code) {
|
291 | CodeSafetyChecker checker = codeSafetyChecker(); |
292 | checker.checkCode(code); |
293 | ret checker.verbalCheckResult(); |
294 | } |
295 | |
296 | bool isCodeSafe(S code) {
|
297 | CodeSafetyChecker checker = codeSafetyChecker(); |
298 | checker.checkCode(code); |
299 | ret checker.isSafe(); |
300 | } |
301 | |
302 | S prepareCode(S code, GazellePost post) {
|
303 | code = tok_addReturn(code); |
304 | |
305 | // define implicit vars and functions |
306 | |
307 | // post = text of parent post |
308 | if (containsJavaToken(code, "post")) {
|
309 | long ref = gazelle_firstPostRef(post.id); |
310 | if (ref != 0) |
311 | code = "S post = " + quote(gazelle_text(ref)) + ";\n" + code; |
312 | } |
313 | |
314 | // post = text of grandparent post |
315 | if (containsJavaToken(code, "post2")) {
|
316 | long ref = gazelle_firstPostRef(gazelle_firstPostRef(post.id)); |
317 | if (ref != 0) |
318 | code = "S post2 = " + quote(gazelle_text(ref)) + ";\n" + code; |
319 | } |
320 | |
321 | if (containsJavaToken(code, "getAllPosts")) |
322 | code = [[ |
323 | embedded Cl<GazellePost> getAllPosts() {
|
324 | ret (Cl) quickImport(dm_call(dm_current_generic(), "getAllPosts")); |
325 | } |
326 | ]] + code; |
327 | |
328 | if (containsJavaToken(code, "createPost")) |
329 | code = [[ |
330 | embedded O createPost(O... _) {
|
331 | ret dm_call(dm_current_generic(), "createPost", _); |
332 | } |
333 | ]] + code; |
334 | |
335 | // optimize gazelle_text |
336 | if (containsJavaToken(code, "gazelle_text")) {
|
337 | code = [[ |
338 | embedded S gazelle_text(long id) {
|
339 | ret (S) dm_call(dm_current_generic(), "getPostText", id); |
340 | } |
341 | ]] + code; |
342 | } |
343 | |
344 | ret code; |
345 | } |
346 | |
347 | O html(IWebRequest req) {
|
348 | new Matches m; |
349 | |
350 | if (eqic(req.uri(), "/favicon.ico")) |
351 | ret serveFile(loadLibrary(#1400439), faviconMimeType()); |
352 | |
353 | if (startsWith(req.uri(), "/chatBotReply/", m) && isInteger(m.rest())) {
|
354 | req.noSpam(); |
355 | long postID = parseLong(m.rest()); |
356 | GazellePost post = getPost(postID); |
357 | if (post == null) ret serve404("Post " + postID + " not found");
|
358 | O code = codeForPost(post); |
359 | if (code == couldntLoadCode) ret withHeader(serve500("Couldn't load code for post"));
|
360 | S q = req.params().get("q"); // user input
|
361 | S cookie = req.params().get("cookie");
|
362 | if (empty(q)) ret withHeader(serveText(""));
|
363 | |
364 | S answer; |
365 | if (implementsInterfaceShortNamed IF1(code)) // stateless bot |
366 | answer = evalWithTimeoutOrFail(evalTimeout, () -> strOrEmpty(callF(code, q))); |
367 | else {
|
368 | print("Stateful bot. cookie: " + cookie);
|
369 | if (empty(cookie)) ret withHeader(serve500("Need cookie for stateful bot"));
|
370 | Conversation conv = uniq(Conversation, +cookie); |
371 | O instance = code; |
372 | if (instance == null) ret withHeader(serve500("No bot instance"));
|
373 | print("Stateful bot instance: " + instance);
|
374 | if (nempty(conv.dataStruct)) {
|
375 | O data = unstructureInRealm(conv.dataStruct, instance); // hopefully this is safe |
376 | print("Unstructured: " + data);
|
377 | copyAllThisDollarFields(instance, data); // probably not needed anymore |
378 | instance = data; |
379 | } |
380 | O _instance = instance; |
381 | answer = evalWithTimeoutOrFail(evalTimeout, () -> strOrEmpty(call(_instance, "answer", q))); |
382 | cset(conv, dataStruct := structure(instance)); |
383 | print("Structured: " + conv.dataStruct);
|
384 | } |
385 | |
386 | ret withHeader(serveJSON(litorderedmap(answer := shorten(maxBotAnswerLength, answer)))); |
387 | } |
388 | |
389 | if (startsWith(req.uri(), "/transpilation/", m) && isInteger(m.rest())) {
|
390 | long postID = parseLong(m.rest()); |
391 | GazellePost post = getPost(postID); |
392 | if (post == null) ret serve404("Post " + postID + " not found");
|
393 | S src = loadTextFile(transpilationFile(post.id)); |
394 | if (empty(src)) |
395 | src = "No transpilation found for post " + postID + "." + |
396 | (!post.isJavaXCode() ? " Code is not a code post." : " Please try \"Touch post\" and wait a few seconds"); |
397 | else pcall {
|
398 | src = javaPrettyPrint(src); |
399 | } |
400 | ret serveText(src); |
401 | } |
402 | ret "Bot here"; |
403 | } |
404 | |
405 | O codeForPost(GazellePost post) {
|
406 | lock codeLoadLock; |
407 | O code = loadedCodePosts.get(post.id); |
408 | if (code == null) {
|
409 | try {
|
410 | S codeText = post.text; |
411 | if (!isCodeSafe(codeText)) fail("Code is not safe: " + codeSafetyCheckResult(codeText));
|
412 | codeText = prepareCode(codeText, null); |
413 | code = evalCode(codeText, post); |
414 | } catch print e {
|
415 | code = couldntLoadCode; |
416 | } |
417 | loadedCodePosts.put(post.id, code); |
418 | } |
419 | ret code; |
420 | } |
421 | |
422 | File transpilationFile(long postID) {
|
423 | ret programFile("Post-Transpilations/" + postID + ".java");
|
424 | } |
425 | |
426 | O withHeader(O response) {
|
427 | call(response, 'addHeader, "Access-Control-Allow-Origin", "*"); |
428 | ret response; |
429 | } |
430 | } |
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: | #1030204 |
| Snippet name: | Gazelle.rocks Multi-Bot [working backup before "auto-bots"] |
| Eternal ID of this version: | #1030204/1 |
| Text MD5: | 7f540a9c6f19da7f179d540abc00fb62 |
| Transpilation MD5: | 4a0ea6a63483a9ccd943345bb5c6a6d9 |
| 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-16 14:28:06 |
| Source code size: | 14947 bytes / 430 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 371 / 475 |
| Referenced in: | [show references] |