dev: add judge0 exec and polling

This commit is contained in:
KernelDeimos
2025-01-28 12:55:54 -05:00
parent 0fac03a05a
commit 5718071c08
3 changed files with 100 additions and 9 deletions

View File

@@ -12,6 +12,17 @@ class Judge0Client {
return await this.get_('/about');
}
async create_submission ({ lang_id, code }) {
return await this.post_('/submissions', {
language_id: lang_id,
source_code: code,
});
}
async get_submission (id) {
return await this.get_(`/submissions/${id}`);
}
async get_ (path) {
if ( ! path.startsWith('/') ) {
path = `/${path}`;
@@ -27,6 +38,24 @@ class Judge0Client {
return resp.data;
}
async post_ (path, data) {
if ( ! path.startsWith('/') ) {
path = `/${path}`;
}
const resp = await axios.request({
method: 'POST',
url: `${this.baseURL}${path}`,
headers: {
Authorization: `Bearer ${this.token}`,
'Content-Type': 'application/json',
},
data,
});
return resp.data;
}
}
module.exports = { Judge0Client };

View File

@@ -1,33 +1,90 @@
const putility = require("@heyputer/putility");
const BaseService = require("../../services/BaseService");
const { Judge0Client } = require("./Judge0Client");
const { Context } = require("../../util/context");
class Judge0Service extends BaseService {
_construct () {
this.about_ = {};
}
async _init () {
this.languages = require('./languages/languages');
this.client = new Judge0Client({
token: this.config.token,
});
// this.submissions_ = {};
}
static IMPLEMENTS = {
['puter-exec']: {
async about () {
return this.about ?? (this.about = await this.client.about());
},
async supported () {
return require('./languages/languages');
},
async exec ({ runtime, code }) {
return await this.exec_(runtime, code);
return await this.exec_({ runtime, code });
}
}
}
async _init () {
this.client = new Judge0Client({
token: this.config.token,
});
}
async exec_ ({ runtime, code }) {
const lang_id = (() => {
if ( runtime.startsWith('j0-') ) {
return runtime.slice(3);
}
let lang = this.languages.find((lang) => lang.language === runtime);
if ( lang ) {
return lang.judge0_id;
}
})();
async exec_ (runtime, code) {
//
if ( !lang_id ) {
throw new Error(`Language or runtime not found: ${runtime}`);
}
const result = await this.client.create_submission({
lang_id,
code,
});
const submission_done = new putility.libs.promise.TeePromise();
(async () => {
// Need to poll the submission until it's done
let poll = setInterval(async () => {
const submission = await this.client.get_submission(result.token);
if ( submission.status.id >= 3 ) {
clearInterval(poll);
// this.submissions_[result.id] = submission;
submission_done.resolve(submission);
}
});
// Wait for the submission to be done
const submission = await submission_done;
this.log.noticeme('Submission done', submission);
// Send event
const svc_event = this.services.get('event');
svc_event.emit('puter-exec.submission.done', {
id: submission.token,
actor: Context.get('actor'),
output: submission.stdout,
summary: submission.message,
measures: {
time: submission.time,
memory: submission.memory,
},
aux_outputs: {
compile: submission.compile_output,
},
});
})();
return result;
}
}

View File

@@ -8,6 +8,11 @@ class PuterExecModule extends AdvancedBase {
const ExecInterfaceService = require('./ExecInterfaceService');
services.registerService('__exec-interfaces', ExecInterfaceService);
if ( !! config?.services?.['judge0'] ) {
const Judge0Service = require('./Judge0Service');
services.registerService('judge0', Judge0Service);
}
}
}