评测报告
Seele 在执行评测任务时,会依据用户的配置和评测任务的结构,向用户返回评测报告。评测报告分为三种:进度(progress)报告、错误(error)报告和完成(completed)报告。它们的关系如下图所示。
当用户没有配置进度报告时,Seele 对于每个评测任务只会返回一个错误报告或完成报告。否则,Seele 会在执行过程中先返回若干个进度报告,再在最后返回一个错误报告或完成报告。
报告类型
评测报告基于 JSON 格式,用 TypeScript 表示的类型如下:
interface SeeleProgressReport {
// 对应于评测任务中的 `id`
id: string;
// 区分三种报告的类型
type: "PROGRESS";
// 符合 RFC 3339 规范的时间戳,表示报告的产生时间
report_at: string;
// 对应于评测任务中各个子任务结构的状态
status: Record<string, unknown>;
}
interface SeeleErrorReport {
// 对应于评测任务中的 `id`
// 当提交的 yaml 语法有误时,Seele 无法解析 `id`,此属性会被设为空
id?: string;
// 区分三种报告的类型
type: "ERROR";
// 错误信息
error: string;
}
interface SeeleCompletedReport {
// 对应于评测任务中的 `id`
id: string;
// 区分三种报告的类型
type: "COMPLETED";
// 符合 RFC 3339 规范的时间戳,表示报告的产生时间
report_at: string;
// 对应于评测任务中各个子任务结构的状态
status: Record<string, unknown>;
}
错误报告
错误报告表示 Seele 在执行评测任务的过程中出现了预期之外的错误,包括评测系统内部错误以及动作任务的异常报错。此时,用户应该认为这次评测任务的执行失败,且结果无效。在下面的例子中,我们提交了一个不符合 Seele 规范的评测任务。
id: example
steps: 114514
Seele 返回了下列的错误报告:
{
"type": "ERROR",
"error": "Error parsing the submission: steps: invalid type: integer `114514`, expected a map at line 1 column 8"
}
完成报告
当 Seele 在执行评测任务的过程中没有出现预期之外的错误,执行结束后会返回一份完成报告,其中会提供各个子任务的执行状况,包括状态以及一些额外的信息。即使一些子任务执行进入了
FAILED
态而不是 SUCCESS
态,只要没有出现上述的预期之外的错误,Seele 仍然会返回完成报告。
在完成报告中,各个任务对应的属性的结构关系与评测任务中的定义是一致的。例如,一个在评测任务的 .steps.first
中定义的任务,在完成报告中仍然位于 .steps.first
。
在下面的例子中,我们运行了一个 Python 容器,使用 Python 计算 1 / 0
的值。
steps:
prepare:
action: "seele/add-file@1"
files:
- path: "main.py"
plain: |
print(f"{1/0}")
run:
action: "seele/run-judge/run@1"
image: "python:alpine"
command: "python main.py"
files: ["main.py"]
fd:
stderr: "err.txt"
report:
embeds:
- path: "err.txt"
field: output
truncate_kib: 8
执行完毕后,Seele 返回了如下的完成报告。其中,运行评测程序的动作任务进入了 FAILED
态,并返回了我们指定的文件的内容,文件内容中承载了 Python 的输出。
{
"id": "rQrUL3UXOrAAUBgE",
"type": "COMPLETED",
"report_at": "2023-03-26T03:02:21.001006976Z",
"status": {
"submitted_at": "2023-03-26T03:02:20.883136036Z",
"id": "rQrUL3UXOrAAUBgE",
"steps": {
"prepare": {
"status": "SUCCESS",
"report": {
"run_at": "2023-03-26T03:02:20.884004210Z",
"time_elapsed_ms": 0,
"type": "add_file"
},
"embeds": {}
},
"run": {
"status": "FAILED",
"report": {
"run_at": "2023-03-26T03:02:20.884799397Z",
"time_elapsed_ms": 115,
"type": "run_container",
"status": "RUNTIME_ERROR",
"exit_code": 1,
"wall_time_ms": 69,
"cpu_user_time_ms": 8,
"cpu_kernel_time_ms": 16,
"memory_usage_kib": 13640
},
"embeds": {
"output": "Traceback (most recent call last):\n File \"/seele/main.py\", line 1, in <module>\n print(f\"{1/0}\")\n ~^~\nZeroDivisionError: division by zero\n"
}
}
}
}
}
进度报告
为了让用户实时地获取评测任务的执行状态,带来更好的用户体验,Seele 提供了进度报告的支持。对于顺序任务、并发任务和动作任务,通过向
progress
属性传入 true
可为此任务添加标识。当 Seele 完成执行此子任务后,它会对整个评测任务生成一份进度报告。
仍然以上面的运行 Python 代码为例子,我们在 prepare
子任务中添加了 progress: true
:
steps:
prepare:
progress: true
action: "seele/add-file@1"
files:
- path: "main.py"
plain: |
print(f"{1/0}")
run:
action: "seele/run-judge/run@1"
image: "python:alpine"
command: "python main.py"
files: ["main.py"]
fd:
stderr: "err.txt"
report:
embeds:
- path: "err.txt"
field: output
truncate_kib: 8
Seele 会在返回上述的完成报告之前,返回如下所示的一份进度报告。在进度报告中,各个任务对应的属性的结构关系与评测任务中的定义是一致的。
{
"id": "NlkFFgPUMAbqUvWc",
"type": "PROGRESS",
"report_at": "2023-03-26T03:22:39.100246877Z",
"status": {
"submitted_at": "2023-03-26T03:22:39.098968341Z",
"id": "NlkFFgPUMAbqUvWc",
"steps": {
"prepare": {
"status": "SUCCESS",
"report": {
"run_at": "2023-03-26T03:22:39.099682421Z",
"time_elapsed_ms": 0,
"type": "add_file"
},
"embeds": {}
},
"run": {
"status": "RUNNING",
"embeds": {}
}
}
}
}
出于性能原因,Seele 不保证用户通过 progress: true
定义每一个的进度报告产生点都能对应一份进度报告。