Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

提交版本

一次提交以哈希声明你要参赛的两个文件,并说明它是你的哪个模型的版本。API 会记录一个新版本,并返回两个一次性的上传 URL。你把文件 PUT 到这两个 URL,然后由审核和一场不计分的试赛决定这个版本是否成为该模型的参赛中版本。

平台自己不保存任何字节,也不从你那里下载任何东西,所以文件由你自己上传,平台上也没有任何进程需要抓取允许列表。

先创建模型。提交从不创建模型:平台会以 unknown_model 拒绝未知的模型,这样一个拼写错误就不会开出第二条版本线,带着它自己的版本号和评分。

准备两个文件

文件内容
model.onnx自包含的 ONNX 模型
manifest.json计算图的输入与输出,以及每个输入对应的一个适配器

不需要打任何发布版本,也不需要拥有任何仓库。模型文件本身就是审计记录。平台把你的字节保存在 models/<version_id>/model.onnx 下,这个键由平台根据版本 id 生成。没有人能给这个键改标签,你也无法删除它,读取该版本的每一方解析到的都是同一个文件。

捷径:网站的表单

登录后打开 /submit。选择模型,选择两个文件,按下按钮。表单会提交到网站当前显示的赛季。如果你还没有模型,它会先把你带到个人主页上的“新建模型”表单。然后页面会:

  1. 读取每个文件,并用浏览器自带的 crypto.subtle 计算它的 SHA-256;
  2. 把这两个摘要作为提交 POST 出去;
  3. 从你的浏览器把两个文件 PUT 到对象存储。这一步没有任何东西经过网站,网站自己不保存任何字节。

摘要和字节来自同一次读取,所以两者不可能不一致:页面对一个缓冲区计算哈希,然后上传的就是同一个缓冲区。你选择文件时页面会显示两个摘要,因为它们就是契约,若被拒绝,原因中会指明它们。

如果传输失败(请求被拦截、代理、防火墙),那么平台仍然会记录这个版本,页面会把它正在使用的两个 URL 对应的 curl 命令交给你。

完整做法:API

要用脚本提交,就自己发出同样的调用。

对最终的两个文件分别计算 SHA-256:Linux 上用 sha256sum model.onnx manifest.json,macOS 上用 shasum -a 256 model.onnx manifest.json,并在请求中给每个摘要加上 sha256: 前缀。JSON 里改动一个空白字符也会改变哈希。

API 通过 HttpOnly 的 soma_session 浏览器 Cookie 认证身份,没有独立的 API 令牌,所以脚本要在浏览器里同源运行,而不是在 shell 中。把模型 id 和两个示例哈希替换掉:

const response = await fetch('/v1/submissions', {
  method: 'POST',
  credentials: 'same-origin',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({
    game: 'ants',
    season: 'summer-2026',              // 赛季的 slug;省略则为精选赛季
    model: '<创建调用返回的 model_id>',
    weights_hash: 'sha256:<64 个十六进制字符>',
    manifest_hash: 'sha256:<64 个十六进制字符>',
    note: 'wider second layer'          // 可选
  })
});
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
console.log(result);

model 是创建调用返回的 model_id,GET /v1/models 会列出你的所有模型。season 是你要参加的赛季的 slug;不填时版本会进入该游戏的精选赛季,而在多个赛季同时进行时,那不一定是你想要的那个。note 是留给你自己在版本页面上回看的备注,最多一小段。

哈希必须是真实的 64 位值;占位符是故意写成无效的。成功的响应状态为 201,带有字段 version_id、model_id、model、version、status、season、weights_hash、manifest_hash 和 upload。记下版本 ID,以便跟踪这个版本。

上传两个文件

网站的表单会替你完成这一步。201 响应中带有:

"upload": {
  "model_onnx":    "https://…/models/<version_id>/model.onnx?X-Amz-…",
  "manifest_json": "https://…/models/<version_id>/manifest.json?X-Amz-…",
  "expires_in": "30m"
}

把每个文件 PUT 到它的 URL,以文件作为完整的请求体,不带任何请求头或凭据:

curl -T model.onnx    "$MODEL_URL"
curl -T manifest.json "$MANIFEST_URL"

从第一次 POST 起,你的版本有三十分钟让两个文件落地。在这个窗口内用相同的两个哈希再次 POST 提交,同一个版本会以 200 响应并给出新的 URL,它们与窗口同时过期,所以一次失败的 PUT 不会浪费你的版本号。窗口过后,或者在一个版本处理中时用不同的哈希提交,平台会以 version_in_flight 响应。

两个文件都落地之前,什么都不会发生。审核会在窗口期内一直等它们。窗口关闭时仍缺少文件的版本会被以 ARTIFACT_MISSING 或 MANIFEST_MISSING 拒绝。初次参赛的人最容易犯这个错误。

平台会对收到的文件重新计算哈希。SHA-256 与你声明不符的文件会被审核拒绝。正是重新计算哈希让签名上传 URL 可以放心发放,而且平台会核对你的声明两次:节点核对计算图的摘要,数据库核对清单的摘要。

版本号按模型各自计数,由平台分配。你的第二个模型的第一个版本是 v1。如果一条版本线因为你之前有过另一个模型而从 4 开始,它的版本号就会让版本页面无法解释。

赛季与候选版本的限制

版本进入其提交所指定的赛季,或者精选赛季;不处于 open 状态的赛季会以 season_not_open 拒绝它。受限赛季只接受它的参与者参赛,而赛季自己的管理员不能参加它(赛季)。其他限制由各赛季自行声明(赛季列出了完整清单),模型数、版本数和提交间隔的每一项限制,平台在请求前和请求后都会用同样的措辞报告。

每个模型在每个赛季同一时间只能有一个 testing 或 verified 版本。有三个模型的参赛者可以同时有三个版本在审核中,一个模型也可以在两个赛季各有一个版本在审核中;赛季还可以限制你同时处理中的版本总数。模型的参赛中版本不会阻止向它提交替换版本。

在你发出请求之前,GET /v1/games/{game}/submission 会报告你相对上述每一条规则的状态,措辞与拒绝时相同。它读取精选赛季,或 ?season=<slug> 指定的那个赛季。

从先前的赛季重新报名

在某个赛季参过赛的条目可以用相同的文件进入另一个赛季,无需上传:POST /v1/submissions/reenter,带上 {game, model, season, from}。新版本为 testing,审核和试赛会按新赛季的规则、重量级、记忆和引擎重新判定它。详见进入新赛季。

接下来会发生什么

关注版本的页面,或读取 GET /v1/me/versions/{version_id},它只属于你,并且从不缓存。状态从 testing 开始,阶段为 queued 或 verifying。通过审核后变为 verified 和 awaiting_trial;通过试赛后晋级为 active,从那时起公开的 GET /v1/versions/{version_id} 也会显示它(在私有赛季中,只有 GET /v1/private/versions/{version_id} 会显示)。

请求错误和之后的拒绝是两回事。缺少哈希返回 400;赛季冲突或重复/候选版本冲突返回 409;会话无效返回 401。先修正请求再重试。如果请求成功而之后的检查失败,请读取 reject_reason 和拒绝原因参考。审核是异步的,请耐心等待;再次提交并不会让它更快。