openroboto-protocol 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/PKG-INFO +5 -5
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/README.md +4 -4
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/pyproject.toml +15 -8
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/schemas.py +99 -11
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/status.py +34 -15
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/LICENSE +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/__init__.py +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/commitment.py +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/constants.py +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/model_format.py +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/model_hash.py +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/py.typed +0 -0
- {openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/seed.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openroboto-protocol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Shared protocol contract for the OpenRoboto subnet on Bittensor (netuid 80): seed derivation, commitment codec, model hashing, status vocabulary and API schemas.
|
|
5
5
|
Keywords: bittensor,openroboto,subnet,protocol
|
|
6
6
|
Author: OpenRoboto
|
|
@@ -31,9 +31,9 @@ Pin the exact version. A floating range means two sides of the subnet can resolv
|
|
|
31
31
|
to different code, which is the failure this package was created to prevent.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
uv add "openroboto-protocol==0.
|
|
34
|
+
uv add "openroboto-protocol==0.2.0"
|
|
35
35
|
# or
|
|
36
|
-
pip install "openroboto-protocol==0.
|
|
36
|
+
pip install "openroboto-protocol==0.2.0"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
```python
|
|
@@ -51,7 +51,7 @@ seed and decode a commitment; that must not cost a `pydantic-core` wheel build o
|
|
|
51
51
|
GPU box. Only `schemas.py` needs pydantic, and only the backend needs `schemas.py`:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
uv add "openroboto-protocol[schemas]==0.
|
|
54
|
+
uv add "openroboto-protocol[schemas]==0.2.0" # backend only
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## Why this package exists
|
|
@@ -121,7 +121,7 @@ The version number *is* the contract version.
|
|
|
121
121
|
| `minor` | New optional field. Old data missing the key **must** have a default |
|
|
122
122
|
| `major` | Breaking change. Requires an on-chain data migration plan and review |
|
|
123
123
|
|
|
124
|
-
Consumers pin an exact version (`openroboto-protocol==0.
|
|
124
|
+
Consumers pin an exact version (`openroboto-protocol==0.2.0`). Floating versions are
|
|
125
125
|
rejected in CI, as is any vendored copy of this code.
|
|
126
126
|
|
|
127
127
|
## What consumers must add to their own CI
|
|
@@ -9,9 +9,9 @@ Pin the exact version. A floating range means two sides of the subnet can resolv
|
|
|
9
9
|
to different code, which is the failure this package was created to prevent.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
uv add "openroboto-protocol==0.
|
|
12
|
+
uv add "openroboto-protocol==0.2.0"
|
|
13
13
|
# or
|
|
14
|
-
pip install "openroboto-protocol==0.
|
|
14
|
+
pip install "openroboto-protocol==0.2.0"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
```python
|
|
@@ -29,7 +29,7 @@ seed and decode a commitment; that must not cost a `pydantic-core` wheel build o
|
|
|
29
29
|
GPU box. Only `schemas.py` needs pydantic, and only the backend needs `schemas.py`:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
uv add "openroboto-protocol[schemas]==0.
|
|
32
|
+
uv add "openroboto-protocol[schemas]==0.2.0" # backend only
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## Why this package exists
|
|
@@ -99,7 +99,7 @@ The version number *is* the contract version.
|
|
|
99
99
|
| `minor` | New optional field. Old data missing the key **must** have a default |
|
|
100
100
|
| `major` | Breaking change. Requires an on-chain data migration plan and review |
|
|
101
101
|
|
|
102
|
-
Consumers pin an exact version (`openroboto-protocol==0.
|
|
102
|
+
Consumers pin an exact version (`openroboto-protocol==0.2.0`). Floating versions are
|
|
103
103
|
rejected in CI, as is any vendored copy of this code.
|
|
104
104
|
|
|
105
105
|
## What consumers must add to their own CI
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "openroboto-protocol"
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
|
|
3
|
+
# 版本号**就是**契约版本(README「Versioning is a promise」),消费方钉死的是精确
|
|
4
|
+
# 版本:`openroboto-protocol==0.2.0`。README 和 AGENTS.md 必须同步改 ——
|
|
5
|
+
# `tests/test_version.py` 双向守着(钉版一致 + 0.x 必须写明「不承诺兼容」)。
|
|
6
|
+
#
|
|
7
|
+
# **停在 0.x 是裁决,不是拖延**:backend 和 cli 都还没钉上线版本,也还没真正
|
|
8
|
+
# import 过这个包,冻结版本号只会冻住它碰巧长成的形状。1.0.0 的四条前置条件
|
|
9
|
+
# 写在 AGENTS.md §1② 的可勾清单里。0.x 阶段不承诺兼容,所以像本轮
|
|
10
|
+
# (0.1.0 → 0.2.0)这样收紧 schemas 的哨兵默认值是允许的。
|
|
11
|
+
#
|
|
12
|
+
# ⚠️ 0.1.0 已经发到 PyPI(2026-08-19,OIDC)。PyPI 版本号不可重用,只能往前发。
|
|
13
|
+
version = "0.2.0"
|
|
9
14
|
description = "Shared protocol contract for the OpenRoboto subnet on Bittensor (netuid 80): seed derivation, commitment codec, model hashing, status vocabulary and API schemas."
|
|
10
15
|
readme = "README.md"
|
|
11
16
|
license = "MIT"
|
|
@@ -24,8 +29,10 @@ requires-python = ">=3.11"
|
|
|
24
29
|
dependencies = []
|
|
25
30
|
|
|
26
31
|
classifiers = [
|
|
27
|
-
# 1.0.0 =
|
|
28
|
-
#
|
|
32
|
+
# ⚠️ 未裁决:这一行是按"1.0.0 = 契约已冻结"写的,而版本停在了 0.x 且
|
|
33
|
+
# README / AGENTS.md 明写「不承诺兼容」—— 矿工在 PyPI 页面上看到的就是这一行,
|
|
34
|
+
# 它和版本号今天讲的是两个故事。要么随 1.0.0 一起坐实,要么先降成
|
|
35
|
+
# "4 - Beta"。**不要顺手改**:这是矿工侧的产品判断,需要人来定。
|
|
29
36
|
"Development Status :: 5 - Production/Stable",
|
|
30
37
|
"Intended Audience :: Developers",
|
|
31
38
|
"Programming Language :: Python :: 3.11",
|
|
@@ -363,9 +363,18 @@ class MinerRef(Contract):
|
|
|
363
363
|
class ModelRef(Contract):
|
|
364
364
|
name: str
|
|
365
365
|
hf_repo: str
|
|
366
|
-
#: 得分那次任务的 `hf_commit`(不变量 6
|
|
366
|
+
#: 得分那次任务的 `hf_commit`(不变量 6)。
|
|
367
367
|
#: **禁止回落到该矿工的其它提交** —— 那是事故 C 的形状。
|
|
368
|
-
|
|
368
|
+
#:
|
|
369
|
+
#: ⚠️ **查不到是 `null`,不是空串。** 唯一来源是 40 位 `hf_commit`
|
|
370
|
+
#: (CLI `preflight.py` 强制校验后才上链),所以 `""` 从来不是一个合法的
|
|
371
|
+
#: revision,它只可能表示「后端没查到」。而 `""` 在 HuggingFace 的 URL 语义里
|
|
372
|
+
#: 是「用默认分支」—— 拿它去拼 `.../tree/{revision}` 会静默跳到 main,
|
|
373
|
+
#: 审计方核对的就不是得分那次的 commit 了。`null` 让「没有值」无处可藏。
|
|
374
|
+
#: `None` = 没钉 commit。**空串不行**:前端拼
|
|
375
|
+
#: `huggingface.co/{repo}/tree/{revision}` 时 `""` 会静默落到默认分支
|
|
376
|
+
#: (看起来正常、指向的却是另一份代码),`None` 至少是个响亮的 404。
|
|
377
|
+
revision: Annotated[str, Field(min_length=1)] | None = None
|
|
369
378
|
|
|
370
379
|
|
|
371
380
|
class ScoreStat(Contract):
|
|
@@ -996,7 +1005,13 @@ class QueueStatusTask(Contract):
|
|
|
996
1005
|
hf_commit: str
|
|
997
1006
|
submitted_at: datetime | None = None
|
|
998
1007
|
#: SQL 里已经查了但线上没放进响应,契约要求补上。
|
|
999
|
-
|
|
1008
|
+
#:
|
|
1009
|
+
#: ⚠️ **没有默认值,必填。** 队列里的每一条任务**必然属于某一轮** ——
|
|
1010
|
+
#: 「不知道是哪一轮」不是一个合法状态,`round_num=0` 更不是:第 0 轮不存在,
|
|
1011
|
+
#: 而 0 会被前端和矿工的 curl 当成一个真实轮次去过滤,静默捞回空列表。
|
|
1012
|
+
#: 生产列是 `NOT NULL`、后端恒填、2026-08-19 副本 119 行里 0 条为 0 ——
|
|
1013
|
+
#: 这个默认值一行都触发不到,留着只会让「忘了填」变得可表达。
|
|
1014
|
+
round_num: int
|
|
1000
1015
|
reason: Reason | None = None
|
|
1001
1016
|
#: 进度条数据。契约卡里管它叫 "progress",但 history 里同一份数据叫 `detail` ——
|
|
1002
1017
|
#: 同一件事两个名字正是本文件要消灭的东西,统一叫 `detail`。
|
|
@@ -1034,9 +1049,11 @@ class SubmissionHistoryItem(Contract):
|
|
|
1034
1049
|
- 删 `legacy_task_id` / `repo_hash` / `hotkey_tag` / `worker_id`:内部字段。
|
|
1035
1050
|
`repo_hash` 是**抄袭判定的模型指纹**,公开它等于把判定依据交出去。
|
|
1036
1051
|
- 删 `eval_detail`:`detail` 已经是它的解析版,两份重复占了响应体一大截。
|
|
1037
|
-
- **永不返回 `status
|
|
1038
|
-
`submission.status || submission.eval_status || …`,**先读 `status
|
|
1039
|
-
|
|
1052
|
+
- **永不返回 `status`**:前端 `normalizeHistoryStatus()` 写的是
|
|
1053
|
+
`submission.status || submission.eval_status || …`,**先读 `status`** ——
|
|
1054
|
+
同一个响应里出现两个状态键,先读到的恰好是没归一的那个,
|
|
1055
|
+
95 条里 33 条状态显示错误就是这么来的(生产两个来源 2026-08-19 副本实测
|
|
1056
|
+
80 行不一致)。这个响应只许有**一个**状态键。旧实现是在出口
|
|
1040
1057
|
`row.pop("status", None)` 才躲过一劫;这里靠「模型里没有这个字段」保证,
|
|
1041
1058
|
并由 `tests/test_schemas.py` 钉死。
|
|
1042
1059
|
|
|
@@ -1071,9 +1088,35 @@ class SubmissionHistoryItem(Contract):
|
|
|
1071
1088
|
detail: dict[str, Any] | None = None
|
|
1072
1089
|
#: 原样保留 —— 矿工文档教的就是读它。`reason` 是加法,不是替换。
|
|
1073
1090
|
reject_reason: str = ""
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
drand_round:
|
|
1091
|
+
#: 派种子的三件套。⚠️ **没派过种子是 `null`,不是 0 / 空串**,三个字段
|
|
1092
|
+
#: 必须一起有或一起没有 —— 否则会发出 `{seed: null, drand_random: "",
|
|
1093
|
+
#: drand_round: 0}` 这种一个字段说「没有」、隔壁说「有」的响应。
|
|
1094
|
+
#:
|
|
1095
|
+
#: 为什么 0 不行,`drand_round` 是最锋利的那条:drand 官方 API 的
|
|
1096
|
+
#: `/public/0` 返回 **HTTP 200**,内容是**当天最新的那一轮**(`latest` 的别名,
|
|
1097
|
+
#: 2026-08-19 实测)。审计方拿着我们发布的 `drand_round: 0` 照着查,
|
|
1098
|
+
#: 不报错、不 404,会拿到一份今天的信标,然后 `verify_seed()` 必然 False ——
|
|
1099
|
+
#: 而他无从判断是我们作弊还是数据缺失。本包的 `seed.drand_round_url()`
|
|
1100
|
+
#: 已经对 `<= 0` 直接 `raise`,这里再把 0 当默认值发出去就是自相矛盾。
|
|
1101
|
+
#:
|
|
1102
|
+
#: `seed` 同理且更隐蔽:0 是 `derive_seed()` 的**合法输出**
|
|
1103
|
+
#: (`int.from_bytes(sha256[-4:])`,概率 1/2³²),靠「0 一定是假的」区分不了。
|
|
1104
|
+
#: 生产 2026-08-19 副本里 20 条 `seed=0` 全部是「没派过种子」,其中 11 条
|
|
1105
|
+
#: 已经出分进过榜单 —— 它们的评测结果不可复现,而响应里看不出来。
|
|
1106
|
+
#:
|
|
1107
|
+
#: 🔴 **约束只加在能加的两个上,`seed` 故意没有。**
|
|
1108
|
+
#: 去掉默认值只改变「字段被省略时给什么」,**并不拒绝显式传进来的 0** ——
|
|
1109
|
+
#: 而真正把 0 发上线的是生产方(后端仓储层曾写 `seed=m.seed or 0`,
|
|
1110
|
+
#: 主动把 NULL 抹成 0)。所以这里必须是**约束**不是默认值,否则这一整段
|
|
1111
|
+
#: 注释描述的失败全都拦不住,只是看起来拦住了。
|
|
1112
|
+
#:
|
|
1113
|
+
#: `seed` 不能加 `gt=0`:0 是 `derive_seed()` 的**合法输出**
|
|
1114
|
+
#: (`int.from_bytes(sha256[-4:])` 值域含 0,概率 1/2³²)。给它加约束等于
|
|
1115
|
+
#: 有朝一日拒绝一个真实的种子,而那条提交会因此永远进不了榜 —— 比它想防的更贵。
|
|
1116
|
+
#: `seed` 靠的是下面那条三元组校验:单独出现 0 而另外两个是 None,会被拒。
|
|
1117
|
+
seed: int | None = None
|
|
1118
|
+
drand_random: Annotated[str, Field(min_length=1)] | None = None
|
|
1119
|
+
drand_round: Annotated[int, Field(gt=0)] | None = None
|
|
1077
1120
|
#: ⚠️ 抄袭指纹。是否该继续公开待产品判断(spec 06 §7 Q5),
|
|
1078
1121
|
#: 在裁决之前保持线上现状(返回),**不要顺手删也不要顺手加 `repo_hash`**。
|
|
1079
1122
|
model_hash: str = ""
|
|
@@ -1085,6 +1128,42 @@ class SubmissionHistoryItem(Contract):
|
|
|
1085
1128
|
updated_at: datetime | None = None
|
|
1086
1129
|
reason: Reason | None = None
|
|
1087
1130
|
|
|
1131
|
+
@model_validator(mode="after")
|
|
1132
|
+
def _seed_triple_is_all_or_nothing(self) -> SubmissionHistoryItem:
|
|
1133
|
+
"""种子三元组必须**一起有或一起没有**。
|
|
1134
|
+
|
|
1135
|
+
这是 `seed` 唯一的守法 —— 它不能像 `drand_round` 那样加 `gt=0`
|
|
1136
|
+
(0 是 `derive_seed()` 的合法输出),所以只能靠它和同伴的一致性来判。
|
|
1137
|
+
|
|
1138
|
+
挡的是这个形状:`{seed: 0, drand_round: null, drand_random: null}`。
|
|
1139
|
+
单看 `seed` 分不出「种子真的是 0」和「没派过种子」,但配上另外两个就能分:
|
|
1140
|
+
**真派过种子的那次,三个字段必然同时有值**(`derive_seed` 的输入就是
|
|
1141
|
+
block_hash + round + drand_random,缺一个都算不出来)。
|
|
1142
|
+
|
|
1143
|
+
生产 2026-08-19 副本里 20 条 `seed=0` 全部是这个形状,其中 11 条已经出分
|
|
1144
|
+
进过榜单 —— 它们的评测结果不可复现,而响应里看不出来。这条校验让那 11 条
|
|
1145
|
+
在序列化那一刻就炸,而不是等审计方去查 drand 才发现。
|
|
1146
|
+
"""
|
|
1147
|
+
present = [
|
|
1148
|
+
self.seed is not None,
|
|
1149
|
+
self.drand_random is not None,
|
|
1150
|
+
self.drand_round is not None,
|
|
1151
|
+
]
|
|
1152
|
+
if any(present) and not all(present):
|
|
1153
|
+
missing = [
|
|
1154
|
+
name
|
|
1155
|
+
for name, ok in zip(
|
|
1156
|
+
("seed", "drand_random", "drand_round"), present, strict=True
|
|
1157
|
+
)
|
|
1158
|
+
if not ok
|
|
1159
|
+
]
|
|
1160
|
+
raise ValueError(
|
|
1161
|
+
f"种子三元组不完整:缺 {missing}。"
|
|
1162
|
+
"有值的那几个说「派过种子」、缺的那几个说「没派过」,"
|
|
1163
|
+
"而 derive_seed 的输入三者缺一不可 —— 这条记录不可复现"
|
|
1164
|
+
)
|
|
1165
|
+
return self
|
|
1166
|
+
|
|
1088
1167
|
|
|
1089
1168
|
class SubmissionHistoryResponse(Contract):
|
|
1090
1169
|
"""`{success, submissions, total, limit, offset}`。
|
|
@@ -1129,7 +1208,10 @@ class EvalEnvironment(Contract):
|
|
|
1129
1208
|
#: 要么从元数据取、要么删字段 —— 未裁决,保持现状不要再硬编码一次。
|
|
1130
1209
|
sim: str = ""
|
|
1131
1210
|
eval_commit: str = ""
|
|
1132
|
-
seed
|
|
1211
|
+
#: ⚠️ 与 `SubmissionHistoryItem.seed` 同一件事:没派过种子是 `null` 不是 0。
|
|
1212
|
+
#: 这个字段是审计方复现评测的输入,`0` 会被照着跑出一份不一样的结果而不报错。
|
|
1213
|
+
#: 外层 `SubmissionDetail.environment` 本来就是 `| None`,内层跟上才自洽。
|
|
1214
|
+
seed: int | None = None
|
|
1133
1215
|
|
|
1134
1216
|
|
|
1135
1217
|
class SubmissionArtifacts(Contract):
|
|
@@ -1302,7 +1384,13 @@ class Baseline(Contract):
|
|
|
1302
1384
|
model_name: str
|
|
1303
1385
|
hf_repo: str
|
|
1304
1386
|
score: ScoreStat
|
|
1305
|
-
revision
|
|
1387
|
+
#: ⚠️ 与 `ModelRef.revision` 同一件事:基线模型从没人钉过 commit,
|
|
1388
|
+
#: 那是「没有值」不是「空串这个值」。`""` 拼进 HF URL 会跳默认分支 ——
|
|
1389
|
+
#: 基线换了模型,历史榜单会安静地指向新的权重。
|
|
1390
|
+
#: `None` = 没钉 commit。**空串不行**:前端拼
|
|
1391
|
+
#: `huggingface.co/{repo}/tree/{revision}` 时 `""` 会静默落到默认分支
|
|
1392
|
+
#: (看起来正常、指向的却是另一份代码),`None` 至少是个响亮的 404。
|
|
1393
|
+
revision: Annotated[str, Field(min_length=1)] | None = None
|
|
1306
1394
|
|
|
1307
1395
|
|
|
1308
1396
|
class LeaderboardResponse(Contract):
|
|
@@ -9,12 +9,18 @@
|
|
|
9
9
|
|
|
10
10
|
**两套词表不是一回事,混用是事故本身**:
|
|
11
11
|
|
|
12
|
-
- **status
|
|
13
|
-
- **stage
|
|
12
|
+
- **status**:提交走到哪一步。生命周期状态,取值见 `ALL_STATUSES`。
|
|
13
|
+
- **stage**:worker 认领任务后在干什么。进度明细,取值见 `ALL_STAGES`,
|
|
14
14
|
只在 status 为 `evaluating` 期间有意义。
|
|
15
15
|
|
|
16
16
|
`evaluating` 是 **status**;`running` 是 **stage**。它们描述的是同一段时间,
|
|
17
|
-
|
|
17
|
+
但不是同一套词表,任何一边写成另一边的词都会被对方判为非法。
|
|
18
|
+
|
|
19
|
+
⚠️ 这里说的是**词表**,不是承载它的字段名,更不是数据库列名。
|
|
20
|
+
携带生命周期状态的响应字段**逐端点不同** —— `SubmissionRecord` 叫 `status`,
|
|
21
|
+
其余四个模型叫 `eval_status`。哪个端点是哪个,以 `schemas.py` 的
|
|
22
|
+
`STATUS_VALUED_FIELDS` 为准(那张表由 `tests/test_schemas.py` 逐条核对,
|
|
23
|
+
不会和代码漂开)。**不要在这里记第二份。**
|
|
18
24
|
|
|
19
25
|
**线上事实**(2026-08-17 curl `GET /api/v1/submissions/history?limit=500`,117 条):
|
|
20
26
|
|
|
@@ -23,7 +29,10 @@
|
|
|
23
29
|
- `stage` 出现过:`running` 61 / `""` 47 / `downloading` 8 / `prechecking` 1。
|
|
24
30
|
**没有出现过 `evaluating`** —— 对外的规范阶段词是 `running`,这条是裁决依据。
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
**这个模块不负责**:状态怎么持久化(存哪张表哪一列、哪一列是权威)、谁有权改状态、
|
|
33
|
+
超时怎么判。那些是后端的事 —— 而且权威列在数据迁移前后是**相反**的
|
|
34
|
+
(迁移前 `eval_status`,迁移后 `status`),写进这个包等于让一个不可回收的版本号
|
|
35
|
+
去承诺一件别的仓库随时会改的事。
|
|
27
36
|
"""
|
|
28
37
|
|
|
29
38
|
from __future__ import annotations
|
|
@@ -34,7 +43,7 @@ from types import MappingProxyType
|
|
|
34
43
|
from typing import Final
|
|
35
44
|
|
|
36
45
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
-
#
|
|
46
|
+
# 提交状态(生命周期)
|
|
38
47
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
39
48
|
|
|
40
49
|
# 扫链刚看到这条 commitment,还没做任何校验。建行时的默认值(schema.py 的 DEFAULT)。
|
|
@@ -103,7 +112,8 @@ FROZEN_STATUSES: Final[frozenset[str]] = frozenset({STATUS_REJECTED, STATUS_SUPE
|
|
|
103
112
|
# (CASE WHEN eval_status='pending',单向)
|
|
104
113
|
# pending → evaluated / eval_failed 历史真实路径:修复前 update_task_progress
|
|
105
114
|
# 只写 stage 不推状态,全库 0 条 evaluating,
|
|
106
|
-
#
|
|
115
|
+
# 几十条 evaluated 全部是从 pending 直接落的
|
|
116
|
+
# (2026-08-19 副本:evaluated 66 条)。
|
|
107
117
|
# **删掉这条边等于宣布线上历史非法。**
|
|
108
118
|
# pending → superseded submission_db.py:supersede_pending —— 它的
|
|
109
119
|
# WHERE 只有一条 eval_status='pending'
|
|
@@ -168,7 +178,7 @@ def can_transition(current: str, new: str) -> bool:
|
|
|
168
178
|
|
|
169
179
|
|
|
170
180
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
171
|
-
#
|
|
181
|
+
# 评测阶段(进度明细)
|
|
172
182
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
173
183
|
|
|
174
184
|
|
|
@@ -182,10 +192,16 @@ class Stage:
|
|
|
182
192
|
"""
|
|
183
193
|
|
|
184
194
|
#: 对外的规范词。worker 该发这个,API 该返回这个,前端该按这个渲染。
|
|
185
|
-
#: 生产 `GET /api/v1/submissions/history`
|
|
195
|
+
#: 生产 `GET /api/v1/submissions/history` 的 `stage` 字段返回的就是这个词
|
|
196
|
+
#: (2026-08-17 实测)。
|
|
186
197
|
wire: str
|
|
187
|
-
#:
|
|
188
|
-
#:
|
|
198
|
+
#: 历史存储值(带 `benchmark_` 前缀)。出口翻译前的样子,读老数据时会遇到。
|
|
199
|
+
#: ⚠️ **两种写法是并存的**,不是「老数据全带前缀」:2026-08-19 的生产副本里
|
|
200
|
+
#: `running` 38 行、`benchmark_running` 24 行、`downloading` 8 行(裸)、
|
|
201
|
+
#: `benchmark_prechecking` 2 行(带前缀)。所以入口必须两种都收 —— 这正是
|
|
202
|
+
#: `_STAGE_LOOKUP` 把 wire / stored / aliases 三者并列的理由。
|
|
203
|
+
#: **不要**把带前缀的吐给前端 —— 前端的 ACTIVE_STAGES 里没有带前缀的写法,
|
|
204
|
+
#: 认不出就不渲染。
|
|
189
205
|
stored: str
|
|
190
206
|
#: 输入侧还接受的同义词。收下它们纯粹是加法:按公开文档或前端类型写的调用方
|
|
191
207
|
#: 曾经因此收到 400(2026-08-14 实际发生)。
|
|
@@ -236,12 +252,15 @@ def normalize_stage(word: str) -> str | None:
|
|
|
236
252
|
|
|
237
253
|
#: 老状态词 → 现行状态词。
|
|
238
254
|
#:
|
|
239
|
-
#:
|
|
240
|
-
#:
|
|
241
|
-
#:
|
|
242
|
-
#:
|
|
255
|
+
#: 这些词今天还活在生产数据里(2026-08-19 副本实测:`done` 37 / `failed` 4 /
|
|
256
|
+
#: `confirmed` 1 / `enqueued` 17),它们是同一件事在词表统一之前的四套写法。
|
|
257
|
+
#:
|
|
258
|
+
#: 出事的形状不是「有老词」,而是**两套词混进同一个响应**:前端按
|
|
259
|
+
#: `submission.status || submission.eval_status` 读,先读到的恰好是没归一的那个 ——
|
|
260
|
+
#: 2026-08-14 队列页 95 条里 33 条状态显示错误。所以归一必须发生在**一个**地方。
|
|
243
261
|
#:
|
|
244
|
-
#:
|
|
262
|
+
#: **`ALL_STATUSES` 是唯一真源。** 这张表只用于读老数据,不要用它生成新值。
|
|
263
|
+
#: 老词今天具体存在哪个存储位置、归一到哪一步为止,是后端的事,不在本模块的承诺范围内。
|
|
245
264
|
LEGACY_STATUS_ALIASES: Final[Mapping[str, str]] = MappingProxyType(
|
|
246
265
|
{
|
|
247
266
|
"enqueued": STATUS_PENDING,
|
|
File without changes
|
|
File without changes
|
{openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/commitment.py
RENAMED
|
File without changes
|
{openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/constants.py
RENAMED
|
File without changes
|
{openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/model_format.py
RENAMED
|
File without changes
|
{openroboto_protocol-0.1.0 → openroboto_protocol-0.2.0}/src/openroboto_protocol/model_hash.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|