{ "version": 3, "sources": ["../../../../server/artemis/local.ts"], "sourcesContent": ["/**\r\n * Typescript wrapper around the Python Artemis model.\r\n * By Mia.\r\n * @author mia-pi-git\r\n */\r\n\r\nimport * as child_process from 'child_process';\r\nimport {ProcessManager, Streams, Utils, Repl, FS} from '../../lib';\r\nimport {Config} from '../config-loader';\r\nimport {toID} from '../../sim/dex-data';\r\n\r\nclass ArtemisStream extends Streams.ObjectReadWriteStream {\r\n\ttasks = new Set();\r\n\tprivate process: child_process.ChildProcessWithoutNullStreams;\r\n\tconstructor() {\r\n\t\tsuper();\r\n\t\tthis.process = child_process.spawn('python3', [\r\n\t\t\t'-u', FS('server/artemis/model.py').path, Config.debugartemisprocesses ? \"debug\" : \"\",\r\n\t\t].filter(Boolean));\r\n\t\tthis.listen();\r\n\t}\r\n\tlisten() {\r\n\t\tthis.process.stdout.setEncoding('utf8');\r\n\t\tthis.process.stderr.setEncoding('utf8');\r\n\t\tthis.process.stdout.on('data', (data) => {\r\n\t\t\t// so many bugs were created by \\nready\\n\r\n\t\t\tdata = data.trim();\r\n\t\t\tconst [taskId, dataStr] = data.split(\"|\");\r\n\t\t\tif (this.tasks.has(taskId)) {\r\n\t\t\t\tthis.tasks.delete(taskId);\r\n\t\t\t\treturn this.push(`${taskId}\\n${dataStr}`);\r\n\t\t\t}\r\n\t\t\tif (taskId === 'error') { // there was a major crash and the script is no longer running\r\n\t\t\t\tconst info = JSON.parse(dataStr);\r\n\t\t\t\tMonitor.crashlog(new Error(info.error), \"An Artemis script\", info);\r\n\t\t\t\ttry {\r\n\t\t\t\t\tthis.pushEnd(); // push end first so the stream always closes\r\n\t\t\t\t\tthis.process.disconnect();\r\n\t\t\t\t} catch {}\r\n\t\t\t}\r\n\t\t});\r\n\t\tthis.process.stderr.on('data', data => {\r\n\t\t\tif (/Downloading: ([0-9]+)%/i.test(data)) {\r\n\t\t\t\t// this prints to stderr fsr and it should not be throwing\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tMonitor.crashlog(new Error(data), \"An Artemis process\");\r\n\t\t});\r\n\t\tthis.process.on('error', err => {\r\n\t\t\tMonitor.crashlog(err, \"An Artemis process\");\r\n\t\t\tthis.pushEnd();\r\n\t\t});\r\n\t\tthis.process.on('close', () => {\r\n\t\t\tthis.pushEnd();\r\n\t\t});\r\n\t}\r\n\t_write(chunk: string) {\r\n\t\tconst [taskId, message] = Utils.splitFirst(chunk, '\\n');\r\n\t\tthis.tasks.add(taskId);\r\n\t\tthis.process.stdin.write(`${taskId}|${message}\\n`);\r\n\t}\r\n\tdestroy() {\r\n\t\ttry {\r\n\t\t\tthis.process.kill();\r\n\t\t} catch {}\r\n\t\tthis.pushEnd();\r\n\t}\r\n}\r\n\r\nexport const PM = new ProcessManager.StreamProcessManager(module, () => new ArtemisStream(), message => {\r\n\tif (message.startsWith('SLOW\\n')) {\r\n\t\tMonitor.slow(message.slice(5));\r\n\t}\r\n});\r\n\r\nexport class LocalClassifier {\r\n\tstatic readonly PM = PM;\r\n\tstatic readonly ATTRIBUTES: Record = {\r\n\t\tsexual_explicit: {},\r\n\t\tsevere_toxicity: {},\r\n\t\ttoxicity: {},\r\n\t\tobscene: {},\r\n\t\tidentity_attack: {},\r\n\t\tinsult: {},\r\n\t\tthreat: {},\r\n\t};\r\n\tstatic classifiers: LocalClassifier[] = [];\r\n\tstatic destroy() {\r\n\t\tfor (const classifier of this.classifiers) void classifier.destroy();\r\n\t\treturn this.PM.destroy();\r\n\t}\r\n\t/** If stream exists, model is usable */\r\n\tstream?: Streams.ObjectReadWriteStream;\r\n\tenabled = false;\r\n\trequests = new Map void>();\r\n\tlastTask = 0;\r\n\treadyPromise: Promise | null = null;\r\n\tconstructor() {\r\n\t\tLocalClassifier.classifiers.push(this);\r\n\t\tvoid this.setupProcesses();\r\n\t}\r\n\tasync setupProcesses() {\r\n\t\tthis.readyPromise = new Promise(resolve => {\r\n\t\t\tchild_process.exec('python3 -c \"import detoxify\"', (err, out, stderr) => {\r\n\t\t\t\tif (err || stderr) {\r\n\t\t\t\t\tresolve(false);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tresolve(true);\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t});\r\n\t\tconst res = await this.readyPromise;\r\n\t\tthis.enabled = res;\r\n\t\tthis.readyPromise = null;\r\n\t\tif (res) {\r\n\t\t\tthis.stream = PM.createStream();\r\n\t\t\tvoid this.listen();\r\n\t\t}\r\n\t}\r\n\tasync listen() {\r\n\t\tif (!this.stream) return null;\r\n\t\tfor await (const chunk of this.stream) {\r\n\t\t\tconst [rawTaskId, data] = Utils.splitFirst(chunk, '\\n');\r\n\t\t\tconst task = parseInt(rawTaskId);\r\n\t\t\tconst resolver = this.requests.get(task);\r\n\t\t\tif (resolver) {\r\n\t\t\t\tresolver(JSON.parse(data));\r\n\t\t\t\tthis.requests.delete(task);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tdestroy() {\r\n\t\tLocalClassifier.classifiers.splice(LocalClassifier.classifiers.indexOf(this), 1);\r\n\t\treturn this.stream?.destroy();\r\n\t}\r\n\tasync classify(text: string) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!this.stream) return null;\r\n\t\tconst taskId = this.lastTask++;\r\n\t\tconst data = await new Promise(resolve => {\r\n\t\t\tthis.requests.set(taskId, resolve);\r\n\t\t\tvoid this.stream?.write(`${taskId}\\n${text}`);\r\n\t\t});\r\n\t\tfor (const k in data) {\r\n\t\t\t// floats have to be made into strings because python's json.dumps\r\n\t\t\t// doesn't like float32s\r\n\t\t\tdata[k] = parseFloat(data[k]);\r\n\t\t}\r\n\t\treturn data as Record;\r\n\t}\r\n}\r\n\r\n// main module check necessary since this gets required in other non-parent processes sometimes\r\n// when that happens we do not want to take over or set up or anything\r\nif (require.main === module) {\r\n\t// This is a child process!\r\n\tglobal.Config = Config;\r\n\tglobal.Monitor = {\r\n\t\tcrashlog(error: Error, source = 'A local Artemis child process', details: AnyObject | null = null) {\r\n\t\t\tconst repr = JSON.stringify([error.name, error.message, source, details]);\r\n\t\t\tprocess.send!(`THROW\\n@!!@${repr}\\n${error.stack}`);\r\n\t\t},\r\n\t\tslow(text: string) {\r\n\t\t\tprocess.send!(`CALLBACK\\nSLOW\\n${text}`);\r\n\t\t},\r\n\t};\r\n\tglobal.toID = toID;\r\n\tprocess.on('uncaughtException', err => {\r\n\t\tif (Config.crashguard) {\r\n\t\t\tMonitor.crashlog(err, 'A local Artemis child process');\r\n\t\t}\r\n\t});\r\n\t// eslint-disable-next-line no-eval\r\n\tRepl.start(`abusemonitor-local-${process.pid}`, cmd => eval(cmd));\r\n} else if (!process.send) {\r\n\tPM.spawn(Config.localartemisprocesses || 1);\r\n}\r\n"], "mappings": "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", "names": [] }