agent-comparability 0.5.3__tar.gz → 0.5.4__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.
- {agent_comparability-0.5.3/src/agent_comparability.egg-info → agent_comparability-0.5.4}/PKG-INFO +5 -5
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/README.md +4 -4
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/pyproject.toml +29 -29
- agent_comparability-0.5.4/src/agent_comparability/__init__.py +3 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/core.py +10 -5
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/data/SPEC.md +7 -4
- agent_comparability-0.5.4/src/agent_comparability/data/cases-0.4.json +16 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/data/cdr.schema.json +1 -1
- {agent_comparability-0.5.3 → agent_comparability-0.5.4/src/agent_comparability.egg-info}/PKG-INFO +5 -5
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability.egg-info/SOURCES.txt +1 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_cli.py +152 -152
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_evidence.py +126 -126
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_gate_regressions.py +5 -5
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_inspect_log.py +352 -352
- agent_comparability-0.5.3/src/agent_comparability/__init__.py +0 -3
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/LICENSE +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/NOTICE +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/setup.cfg +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/__main__.py +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/data/boundary-cases.json +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/data/frozen-cases-a.json +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/evidence.py +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/inspect_log.py +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/py.typed +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability.egg-info/dependency_links.txt +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability.egg-info/requires.txt +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability.egg-info/top_level.txt +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_properties.py +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_scenarios_001.py +0 -0
- {agent_comparability-0.5.3 → agent_comparability-0.5.4}/tests/test_smoke.py +0 -0
{agent_comparability-0.5.3/src/agent_comparability.egg-info → agent_comparability-0.5.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-comparability
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: Agent evaluation comparability semantics: spec + conformance suite + thin reference implementation
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/Rethymus/agent-comparability
|
|
@@ -255,7 +255,7 @@ CDR — actual output:
|
|
|
255
255
|
"rec:t1/m1/h1",
|
|
256
256
|
"rec:t1/m1/h2"
|
|
257
257
|
],
|
|
258
|
-
"policy_version": "cmp/0.
|
|
258
|
+
"policy_version": "cmp/0.4.0",
|
|
259
259
|
"limitations": [
|
|
260
260
|
"摘要一致仅证明未同步修改,不构成抗恶意篡改(单写信任域)"
|
|
261
261
|
]
|
|
@@ -321,7 +321,7 @@ For **real Inspect EvalLog files** (not frozen cases) use the converter:
|
|
|
321
321
|
|
|
322
322
|
## 12. Specification & Versioning
|
|
323
323
|
|
|
324
|
-
Three distinct lines: software tag (e.g. `v0.5.0`), specification `cmp/0.
|
|
324
|
+
Three distinct lines: software tag (e.g. `v0.5.0`), specification `cmp/0.4.0`
|
|
325
325
|
(inside SPEC), and `agent_comparability.__version__`. Judgment-table changes
|
|
326
326
|
require a spec major bump (SPEC §5). See [CHANGELOG](CHANGELOG.md) for the full tag lineage
|
|
327
327
|
(v0.3.1–v0.3.6 are deprecated candidates kept immutably for the record).
|
|
@@ -355,13 +355,13 @@ No — no network, no credentials, no Docker. It only ever sees the records you
|
|
|
355
355
|
|
|
356
356
|
**Which versions should I depend on?**
|
|
357
357
|
Package `v0.5.0+` (version lines unified and CI-enforced since then); specification
|
|
358
|
-
`cmp/0.
|
|
358
|
+
`cmp/0.4.0` evolves independently — see [§12](#12-specification--versioning).
|
|
359
359
|
|
|
360
360
|
## Project status
|
|
361
361
|
|
|
362
362
|
| Component | Status |
|
|
363
363
|
|---|---|
|
|
364
|
-
| Specification `cmp/0.
|
|
364
|
+
| Specification `cmp/0.4.0` | Stable — frozen conformance set anchors it |
|
|
365
365
|
| Claim types | `score_comparison`, `causal`, `descriptive_registration` (+ unknown → `INCONCLUSIVE`) |
|
|
366
366
|
| Conformance cases | 22 frozen pairs (A + B sets), three separated metrics, all green in CI |
|
|
367
367
|
| Adapters | Inspect AI ✓ · Pydantic Evals ✓ |
|
|
@@ -233,7 +233,7 @@ CDR — actual output:
|
|
|
233
233
|
"rec:t1/m1/h1",
|
|
234
234
|
"rec:t1/m1/h2"
|
|
235
235
|
],
|
|
236
|
-
"policy_version": "cmp/0.
|
|
236
|
+
"policy_version": "cmp/0.4.0",
|
|
237
237
|
"limitations": [
|
|
238
238
|
"摘要一致仅证明未同步修改,不构成抗恶意篡改(单写信任域)"
|
|
239
239
|
]
|
|
@@ -299,7 +299,7 @@ For **real Inspect EvalLog files** (not frozen cases) use the converter:
|
|
|
299
299
|
|
|
300
300
|
## 12. Specification & Versioning
|
|
301
301
|
|
|
302
|
-
Three distinct lines: software tag (e.g. `v0.5.0`), specification `cmp/0.
|
|
302
|
+
Three distinct lines: software tag (e.g. `v0.5.0`), specification `cmp/0.4.0`
|
|
303
303
|
(inside SPEC), and `agent_comparability.__version__`. Judgment-table changes
|
|
304
304
|
require a spec major bump (SPEC §5). See [CHANGELOG](CHANGELOG.md) for the full tag lineage
|
|
305
305
|
(v0.3.1–v0.3.6 are deprecated candidates kept immutably for the record).
|
|
@@ -333,13 +333,13 @@ No — no network, no credentials, no Docker. It only ever sees the records you
|
|
|
333
333
|
|
|
334
334
|
**Which versions should I depend on?**
|
|
335
335
|
Package `v0.5.0+` (version lines unified and CI-enforced since then); specification
|
|
336
|
-
`cmp/0.
|
|
336
|
+
`cmp/0.4.0` evolves independently — see [§12](#12-specification--versioning).
|
|
337
337
|
|
|
338
338
|
## Project status
|
|
339
339
|
|
|
340
340
|
| Component | Status |
|
|
341
341
|
|---|---|
|
|
342
|
-
| Specification `cmp/0.
|
|
342
|
+
| Specification `cmp/0.4.0` | Stable — frozen conformance set anchors it |
|
|
343
343
|
| Claim types | `score_comparison`, `causal`, `descriptive_registration` (+ unknown → `INCONCLUSIVE`) |
|
|
344
344
|
| Conformance cases | 22 frozen pairs (A + B sets), three separated metrics, all green in CI |
|
|
345
345
|
| Adapters | Inspect AI ✓ · Pydantic Evals ✓ |
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=68"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "agent-comparability"
|
|
7
|
-
version = "0.5.
|
|
8
|
-
description = "Agent evaluation comparability semantics: spec + conformance suite + thin reference implementation"
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.10"
|
|
11
|
-
license = {text = "Apache-2.0"}
|
|
12
|
-
dependencies = []
|
|
13
|
-
|
|
14
|
-
[project.urls]
|
|
15
|
-
Homepage = "https://github.com/Rethymus/agent-comparability"
|
|
16
|
-
Repository = "https://github.com/Rethymus/agent-comparability"
|
|
17
|
-
Issues = "https://github.com/Rethymus/agent-comparability/issues"
|
|
18
|
-
Changelog = "https://github.com/Rethymus/agent-comparability/blob/main/CHANGELOG.md"
|
|
19
|
-
|
|
20
|
-
[project.optional-dependencies]
|
|
21
|
-
suite = ["pyyaml", "jsonschema"]
|
|
22
|
-
inspect = ["inspect-ai"]
|
|
23
|
-
pydantic = ["pydantic-evals"]
|
|
24
|
-
|
|
25
|
-
[tool.setuptools.packages.find]
|
|
26
|
-
where = ["src"]
|
|
27
|
-
|
|
28
|
-
[tool.setuptools.package-data]
|
|
29
|
-
agent_comparability = ["data/*"]
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-comparability"
|
|
7
|
+
version = "0.5.4"
|
|
8
|
+
description = "Agent evaluation comparability semantics: spec + conformance suite + thin reference implementation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "Apache-2.0"}
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://github.com/Rethymus/agent-comparability"
|
|
16
|
+
Repository = "https://github.com/Rethymus/agent-comparability"
|
|
17
|
+
Issues = "https://github.com/Rethymus/agent-comparability/issues"
|
|
18
|
+
Changelog = "https://github.com/Rethymus/agent-comparability/blob/main/CHANGELOG.md"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
suite = ["pyyaml", "jsonschema"]
|
|
22
|
+
inspect = ["inspect-ai"]
|
|
23
|
+
pydantic = ["pydantic-evals"]
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.packages.find]
|
|
26
|
+
where = ["src"]
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.package-data]
|
|
29
|
+
agent_comparability = ["data/*"]
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
"""比较判定核心库 cmp/core(cmp/0.
|
|
1
|
+
"""比较判定核心库 cmp/core(cmp/0.4.0 规范参考实现)。
|
|
2
2
|
|
|
3
|
+
相对 0.3.1 的判定表分支修正(#27 裁决方向 B,SPEC §5 允许 minor 新增分支):
|
|
4
|
+
⑤score_comparison 的 INADMISSIBLE 触发移除 same_model——单变量对称性
|
|
5
|
+
(§4 行 2:仅模型不同 → ADMISSIBLE 并排);模型-only 变化由
|
|
6
|
+
score_scale_consistent / zero_semantics_uniform 独立把关(D2 冻结对证明)。
|
|
3
7
|
相对 0.2 的规范符合性修复(依据 SPEC 0.3.1;B 集冻结预期先于本修改存在):
|
|
4
8
|
①同任务性检查入 score 判定优先级 2(B3 暴露 0.2 缺失)
|
|
5
9
|
②评分尺度一致性检查(含 0.3.1 隐式默认规则)(B4/B5 暴露 0.2 缺失)
|
|
6
10
|
③claim 路由补 registration/descriptive 关键词(B2-B5 暴露误判)
|
|
7
11
|
④policy_version 升 cmp/0.3.1(消费方按 SPEC §5 重判旧 CDR)
|
|
8
|
-
判定优先级(SPEC §3.3):INCONCLUSIVE(缺trace) > INADMISSIBLE(
|
|
12
|
+
判定优先级(SPEC §3.3):INCONCLUSIVE(缺trace) > INADMISSIBLE(任务/混淆/尺度) > PARTIAL(零语义) > ADMISSIBLE。
|
|
9
13
|
"""
|
|
10
14
|
|
|
11
15
|
from dataclasses import dataclass, field, asdict
|
|
12
16
|
|
|
13
|
-
POLICY_VERSION = "cmp/0.
|
|
17
|
+
POLICY_VERSION = "cmp/0.4.0"
|
|
14
18
|
TAMPER_LIMITATION = "摘要一致仅证明未同步修改,不构成抗恶意篡改(单写信任域)"
|
|
15
19
|
DEFAULT_SCALE = {"range": "0..1", "unit": "fraction", "verifier": "source"}
|
|
16
20
|
KNOWN_FIELDS = {"task_id", "harness", "model", "reward", "reward_status",
|
|
@@ -92,9 +96,10 @@ def adjudicate(case: dict, requested_claim: str) -> CDR:
|
|
|
92
96
|
cdr.admissibility = "INCONCLUSIVE"
|
|
93
97
|
cdr.supported_claims.append("score_transcription(source_verifier_attested)")
|
|
94
98
|
cdr.unsupported_claims.append("success_rate(含证据缺失记录)")
|
|
95
|
-
elif len(tasks) > 1 or
|
|
99
|
+
elif len(tasks) > 1 or confounded or "score_scale_consistent" in cdr.violated_conditions:
|
|
100
|
+
# 0.4:same_model 移出硬触发(#27 单变量对称性);尺度/零语义条件独立把关
|
|
96
101
|
cdr.admissibility = "INADMISSIBLE"
|
|
97
|
-
cdr.unsupported_claims.append("score_comparison(
|
|
102
|
+
cdr.unsupported_claims.append("score_comparison(任务/混淆/尺度条件冲突)")
|
|
98
103
|
elif any(v.startswith("zero_semantics_uniform") for v in cdr.violated_conditions):
|
|
99
104
|
cdr.admissibility = "PARTIAL"
|
|
100
105
|
cdr.supported_claims.append("per_status_stratified_rate(verified_zero/unresolved_zero 分列)")
|
{agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability/data/SPEC.md
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Agent Evaluation Comparability Specification — cmp/0.
|
|
1
|
+
# Agent Evaluation Comparability Specification — cmp/0.4.0 (Release Candidate)
|
|
2
2
|
|
|
3
3
|
规范状态:候选规范(非行业标准、无第三方认证)。本文是判定语义的单一事实来源;参考实现(`scripts/cmp/core.py`)与一切第三方实现均以本文为准,冲突时以本文裁决。可脱离任何源码阅读。
|
|
4
4
|
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
| 类型 | 语义 |
|
|
33
33
|
|---|---|
|
|
34
34
|
| `causal` | 断言某变量(模型/Harness)**导致**了分数差异 |
|
|
35
|
-
| `score_comparison` |
|
|
35
|
+
| `score_comparison` | 请求对连续分数做(并排或汇总)比较。历史冻结案例集使用早期写法 `score_compare`;参考实现通过下划线归一化与关键词匹配接受两者(实现细节,非规范语义),新代码应一律使用 `score_comparison`。 |
|
|
36
36
|
| `descriptive_registration` | 仅登记条件与分数,不作比较结论 |
|
|
37
37
|
| 其他 | **未支持类型** |
|
|
38
38
|
|
|
@@ -67,7 +67,10 @@
|
|
|
67
67
|
|
|
68
68
|
**`score_comparison`**(按优先级,先命中先裁决):
|
|
69
69
|
1. 违反 `trace_available` → `INCONCLUSIVE`(来源评分转录保留于 `unknown_conditions`,禁止删除)
|
|
70
|
-
2. 违反 `single_variable_isolation` **或** `same_task_id`
|
|
70
|
+
2. 违反 `single_variable_isolation` **或** `same_task_id` → `INADMISSIBLE`
|
|
71
|
+
(**cmp/0.4 修正(#27)**:`same_model` 不再单独触发 INADMISSIBLE——单变量对称性,与
|
|
72
|
+
§4 行 1/行 2 一致;模型-only 变化由 `score_scale_consistent`/`zero_semantics_uniform`
|
|
73
|
+
独立把关。0.3.x 行为见版本记录。)
|
|
71
74
|
3. 违反 `score_scale_consistent` → `INADMISSIBLE`
|
|
72
75
|
4. 违反 `zero_semantics_uniform` → `PARTIAL`(支持按 verified/unresolved 分列的率;禁止合并 fail 率)
|
|
73
76
|
5. 其余 → `ADMISSIBLE`(同尺度连续分数并排)
|
|
@@ -95,7 +98,7 @@
|
|
|
95
98
|
|
|
96
99
|
- **未知字段**:Record 中规范未定义的字段**必须忽略**(不得报错、不得猜测),并在 `unknown_conditions` 记 `ignored_fields:[...]`。
|
|
97
100
|
- **未支持 Claim**:见 §2 —— INCONCLUSIVE + `unrecognized_claim_type`,禁止近似匹配。
|
|
98
|
-
- **Policy 版本**:CDR 必须携带 `policy_version`。消费方遇到 `policy_version` 不认识时**必须丢弃该 CDR 并重新判定**,不得部分信任。本规范为 `cmp/0.3.1
|
|
101
|
+
- **Policy 版本**:CDR 必须携带 `policy_version`。消费方遇到 `policy_version` 不认识时**必须丢弃该 CDR 并重新判定**,不得部分信任。本规范为 `cmp/0.4.0`(0.3→0.4:判定表分支修正——score_comparison 的 INADMISSIBLE 触发移除 `same_model`,实现单变量对称性,裁决记录见 #27;D 冻结对(D1/D2)先于实现冻结)。0.3.1 为 0.3 的澄清版(仅补隐式默认尺度规则)。
|
|
99
102
|
- **规范不兼容变更**:升 minor(0.3→0.4)可新增判定分支;改判定表必须升 major 并迁移案例。
|
|
100
103
|
|
|
101
104
|
---
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"policy_version": "cmp/0.4.0",
|
|
3
|
+
"frozen_note": "D 集(#27 裁决方向 B,先于实现冻结):单变量对称性——模型-only 变化遵循 SPEC §4 行 2。A/B 集与既有预期零改动;D2 证明尺度条件独立把关未被放松。",
|
|
4
|
+
"cases": [
|
|
5
|
+
{"id": "D1", "category": "model-only-variation",
|
|
6
|
+
"records": [
|
|
7
|
+
{"task_id": "t-model-only", "harness": "h1", "model": "model-a", "reward": 0.8, "reward_status": "verified"},
|
|
8
|
+
{"task_id": "t-model-only", "harness": "h1", "model": "model-b", "reward": 0.6, "reward_status": "verified"}],
|
|
9
|
+
"claims": {"score_comparison": "ADMISSIBLE", "causal": "INADMISSIBLE"}},
|
|
10
|
+
{"id": "D2", "category": "model-plus-scale-violation",
|
|
11
|
+
"records": [
|
|
12
|
+
{"task_id": "t-model-scale", "harness": "h1", "model": "model-a", "reward": 0.8, "reward_status": "verified"},
|
|
13
|
+
{"task_id": "t-model-scale", "harness": "h1", "model": "model-b", "reward": 62.0, "reward_status": "verified"}],
|
|
14
|
+
"claims": {"score_comparison": "INADMISSIBLE", "descriptive_registration": "ADMISSIBLE"}}
|
|
15
|
+
]
|
|
16
|
+
}
|
{agent_comparability-0.5.3 → agent_comparability-0.5.4/src/agent_comparability.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-comparability
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: Agent evaluation comparability semantics: spec + conformance suite + thin reference implementation
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/Rethymus/agent-comparability
|
|
@@ -255,7 +255,7 @@ CDR — actual output:
|
|
|
255
255
|
"rec:t1/m1/h1",
|
|
256
256
|
"rec:t1/m1/h2"
|
|
257
257
|
],
|
|
258
|
-
"policy_version": "cmp/0.
|
|
258
|
+
"policy_version": "cmp/0.4.0",
|
|
259
259
|
"limitations": [
|
|
260
260
|
"摘要一致仅证明未同步修改,不构成抗恶意篡改(单写信任域)"
|
|
261
261
|
]
|
|
@@ -321,7 +321,7 @@ For **real Inspect EvalLog files** (not frozen cases) use the converter:
|
|
|
321
321
|
|
|
322
322
|
## 12. Specification & Versioning
|
|
323
323
|
|
|
324
|
-
Three distinct lines: software tag (e.g. `v0.5.0`), specification `cmp/0.
|
|
324
|
+
Three distinct lines: software tag (e.g. `v0.5.0`), specification `cmp/0.4.0`
|
|
325
325
|
(inside SPEC), and `agent_comparability.__version__`. Judgment-table changes
|
|
326
326
|
require a spec major bump (SPEC §5). See [CHANGELOG](CHANGELOG.md) for the full tag lineage
|
|
327
327
|
(v0.3.1–v0.3.6 are deprecated candidates kept immutably for the record).
|
|
@@ -355,13 +355,13 @@ No — no network, no credentials, no Docker. It only ever sees the records you
|
|
|
355
355
|
|
|
356
356
|
**Which versions should I depend on?**
|
|
357
357
|
Package `v0.5.0+` (version lines unified and CI-enforced since then); specification
|
|
358
|
-
`cmp/0.
|
|
358
|
+
`cmp/0.4.0` evolves independently — see [§12](#12-specification--versioning).
|
|
359
359
|
|
|
360
360
|
## Project status
|
|
361
361
|
|
|
362
362
|
| Component | Status |
|
|
363
363
|
|---|---|
|
|
364
|
-
| Specification `cmp/0.
|
|
364
|
+
| Specification `cmp/0.4.0` | Stable — frozen conformance set anchors it |
|
|
365
365
|
| Claim types | `score_comparison`, `causal`, `descriptive_registration` (+ unknown → `INCONCLUSIVE`) |
|
|
366
366
|
| Conformance cases | 22 frozen pairs (A + B sets), three separated metrics, all green in CI |
|
|
367
367
|
| Adapters | Inspect AI ✓ · Pydantic Evals ✓ |
|
{agent_comparability-0.5.3 → agent_comparability-0.5.4}/src/agent_comparability.egg-info/SOURCES.txt
RENAMED
|
@@ -15,6 +15,7 @@ src/agent_comparability.egg-info/requires.txt
|
|
|
15
15
|
src/agent_comparability.egg-info/top_level.txt
|
|
16
16
|
src/agent_comparability/data/SPEC.md
|
|
17
17
|
src/agent_comparability/data/boundary-cases.json
|
|
18
|
+
src/agent_comparability/data/cases-0.4.json
|
|
18
19
|
src/agent_comparability/data/cdr.schema.json
|
|
19
20
|
src/agent_comparability/data/frozen-cases-a.json
|
|
20
21
|
tests/test_cli.py
|
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
"""CLI(python -m agent_comparability)正常/错误路径回归(#16)。
|
|
2
|
-
|
|
3
|
-
退出码契约:
|
|
4
|
-
0 = 产出 CDR(四种 admissibility 均为裁决成功,不是程序失败)
|
|
5
|
-
2 = 输入错误(JSON 语法/契约字段缺失或类型错/文件不可读),诊断必须含出错路径
|
|
6
|
-
无效输入绝不允许被转换为貌似成功的裁决。
|
|
7
|
-
"""
|
|
8
|
-
import json
|
|
9
|
-
import subprocess
|
|
10
|
-
import sys
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
import pytest
|
|
14
|
-
|
|
15
|
-
ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
-
|
|
17
|
-
VALID = {
|
|
18
|
-
"id": "cli-demo",
|
|
19
|
-
"records": [
|
|
20
|
-
{"task_id": "t1", "harness": "h1", "model": "m1", "reward": 1.0, "reward_status": "verified"},
|
|
21
|
-
{"task_id": "t1", "harness": "h2", "model": "m1", "reward": 0.5, "reward_status": "verified"},
|
|
22
|
-
],
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def run_cli(*args):
|
|
27
|
-
return subprocess.run([sys.executable, "-m", "agent_comparability", *args],
|
|
28
|
-
capture_output=True, text=True, cwd=ROOT)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _tmp_case(tmp_path, payload, raw=None, name="case.json"):
|
|
32
|
-
p = tmp_path / name
|
|
33
|
-
p.write_text(raw if raw is not None else json.dumps(payload), encoding="utf-8")
|
|
34
|
-
return str(p)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def test_valid_input_exit0_admissible(tmp_path):
|
|
38
|
-
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", "score_comparison")
|
|
39
|
-
assert r.returncode == 0, r.stderr
|
|
40
|
-
cdr = json.loads(r.stdout)
|
|
41
|
-
assert cdr["admissibility"] == "ADMISSIBLE"
|
|
42
|
-
assert cdr["policy_version"] == "cmp/0.
|
|
43
|
-
assert any("抗恶意篡改" in l for l in cdr["limitations"])
|
|
44
|
-
for k in ("case_id", "requested_claim", "satisfied_conditions", "violated_conditions",
|
|
45
|
-
"unknown_conditions", "supported_claims", "unsupported_claims",
|
|
46
|
-
"evidence_refs", "comparison_scope"):
|
|
47
|
-
assert k in cdr
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@pytest.mark.parametrize("claim,expected", [
|
|
51
|
-
("causal", "INADMISSIBLE"),
|
|
52
|
-
("score_comparison", "ADMISSIBLE"),
|
|
53
|
-
])
|
|
54
|
-
def test_all_verdicts_are_exit0_not_program_failure(tmp_path, claim, expected):
|
|
55
|
-
"""INADMISSIBLE/PARTIAL/INCONCLUSIVE 与 ADMISSIBLE 同为 exit 0 —— 裁决 ≠ 程序失败。"""
|
|
56
|
-
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", claim)
|
|
57
|
-
assert r.returncode == 0, r.stderr
|
|
58
|
-
assert json.loads(r.stdout)["admissibility"] == expected
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_inconclusive_and_partial_exit0(tmp_path):
|
|
62
|
-
miss = {"id": "m", "records": [dict(VALID["records"][0], trajectory_available=False)]}
|
|
63
|
-
r = run_cli(_tmp_case(tmp_path, miss, name="miss.json"), "--claim", "score_comparison")
|
|
64
|
-
assert r.returncode == 0 and json.loads(r.stdout)["admissibility"] == "INCONCLUSIVE"
|
|
65
|
-
mixed = {"id": "x", "records": [
|
|
66
|
-
{"task_id": "t", "harness": "a", "model": "m", "reward": 0.0, "reward_status": "verified"},
|
|
67
|
-
{"task_id": "t", "harness": "b", "model": "m", "reward": 0.0, "reward_status": "unresolved_zero"}]}
|
|
68
|
-
r2 = run_cli(_tmp_case(tmp_path, mixed, name="mixed.json"), "--claim", "score_comparison")
|
|
69
|
-
assert r2.returncode == 0 and json.loads(r2.stdout)["admissibility"] == "PARTIAL"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_unknown_claim_type_exit0_inconclusive(tmp_path):
|
|
73
|
-
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", "leaderboard_ranking")
|
|
74
|
-
assert r.returncode == 0
|
|
75
|
-
cdr = json.loads(r.stdout)
|
|
76
|
-
assert cdr["admissibility"] == "INCONCLUSIVE"
|
|
77
|
-
assert any("unrecognized_claim_type" in u for u in cdr["unknown_conditions"])
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def test_unknown_fields_not_error_but_recorded(tmp_path):
|
|
81
|
-
"""SPEC §5:未知字段必须忽略(不报错)并记入 unknown_conditions —— CLI 不得比规范更严。"""
|
|
82
|
-
payload = json.loads(json.dumps(VALID))
|
|
83
|
-
payload["records"][0]["custom_extra"] = "keep-me-ignored"
|
|
84
|
-
r = run_cli(_tmp_case(tmp_path, payload), "--claim", "score_comparison")
|
|
85
|
-
assert r.returncode == 0, r.stderr
|
|
86
|
-
cdr = json.loads(r.stdout)
|
|
87
|
-
assert cdr["admissibility"] == "ADMISSIBLE"
|
|
88
|
-
assert any("ignored_fields" in u and "custom_extra" in u for u in cdr["unknown_conditions"])
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def test_output_file_flag(tmp_path):
|
|
92
|
-
out = tmp_path / "cdr.json"
|
|
93
|
-
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", "score_comparison", "-o", str(out))
|
|
94
|
-
assert r.returncode == 0, r.stderr
|
|
95
|
-
assert r.stdout.strip() == ""
|
|
96
|
-
cdr = json.loads(out.read_text(encoding="utf-8"))
|
|
97
|
-
assert cdr["admissibility"] == "ADMISSIBLE"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# ---------- 错误路径:全部 exit 2 + 诊断含路径 ----------
|
|
101
|
-
|
|
102
|
-
def test_missing_input_file_exit2(tmp_path):
|
|
103
|
-
r = run_cli(str(tmp_path / "nope.json"), "--claim", "score_comparison")
|
|
104
|
-
assert r.returncode == 2
|
|
105
|
-
assert "nope.json" in r.stderr
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def test_malformed_json_exit2(tmp_path):
|
|
109
|
-
r = run_cli(_tmp_case(tmp_path, None, raw="{not valid json"), "--claim", "score_comparison")
|
|
110
|
-
assert r.returncode == 2
|
|
111
|
-
assert "JSON" in r.stderr
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@pytest.mark.parametrize("payload,needle", [
|
|
115
|
-
({"records": VALID["records"]}, "id"), # 缺 id
|
|
116
|
-
({"id": "x"}, "records"), # 缺 records
|
|
117
|
-
({"id": "x", "records": []}, "records"), # 空 records
|
|
118
|
-
({"id": 42, "records": VALID["records"]}, "id"), # id 类型错
|
|
119
|
-
({"id": "x", "records": [{"harness": "h", "model": "m", "reward": 1.0,
|
|
120
|
-
"reward_status": "verified"}]}, "task_id"), # 缺必填字段
|
|
121
|
-
({"id": "x", "records": [dict(VALID["records"][0], reward="high")]}, "reward"), # reward 类型错
|
|
122
|
-
({"id": "x", "records": [dict(VALID["records"][0], reward=True)]}, "reward"), # bool 不是数值
|
|
123
|
-
({"id": "x", "records": [dict(VALID["records"][0], reward_status=0)]}, "reward_status"), # status 类型错
|
|
124
|
-
])
|
|
125
|
-
def test_contract_violations_exit2_with_path(tmp_path, payload, needle):
|
|
126
|
-
r = run_cli(_tmp_case(tmp_path, payload), "--claim", "score_comparison")
|
|
127
|
-
assert r.returncode == 2, f"期望 exit 2,实际 {r.returncode};stdout={r.stdout!r}"
|
|
128
|
-
assert needle in r.stderr, r.stderr
|
|
129
|
-
assert "records[0]" in r.stderr or needle == "records" or needle == "id" and "records" not in r.stderr
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def test_bad_optional_field_types_exit2(tmp_path):
|
|
133
|
-
rec = dict(VALID["records"][0], trajectory_available="yes")
|
|
134
|
-
r = run_cli(_tmp_case(tmp_path, {"id": "x", "records": [rec]}), "--claim", "score_comparison")
|
|
135
|
-
assert r.returncode == 2 and "trajectory_available" in r.stderr
|
|
136
|
-
rec2 = dict(VALID["records"][0], score_scale="0..1")
|
|
137
|
-
r2 = run_cli(_tmp_case(tmp_path, {"id": "x", "records": [rec2]}, name="c2.json"),
|
|
138
|
-
"--claim", "score_comparison")
|
|
139
|
-
assert r2.returncode == 2 and "score_scale" in r2.stderr
|
|
140
|
-
rec3 = dict(VALID["records"][0], repetition_count=0)
|
|
141
|
-
r3 = run_cli(_tmp_case(tmp_path, {"id": "x", "records": [rec3]}, name="c3.json"),
|
|
142
|
-
"--claim", "score_comparison")
|
|
143
|
-
assert r3.returncode == 2 and "repetition_count" in r3.stderr
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def test_missing_claim_flag_exit2(tmp_path):
|
|
147
|
-
r = run_cli(_tmp_case(tmp_path, VALID))
|
|
148
|
-
assert r.returncode == 2 # argparse required 参数缺失 → 2(argparse 惯例),不得默认猜 claim
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def test_no_args_exit2():
|
|
152
|
-
assert run_cli().returncode == 2
|
|
1
|
+
"""CLI(python -m agent_comparability)正常/错误路径回归(#16)。
|
|
2
|
+
|
|
3
|
+
退出码契约:
|
|
4
|
+
0 = 产出 CDR(四种 admissibility 均为裁决成功,不是程序失败)
|
|
5
|
+
2 = 输入错误(JSON 语法/契约字段缺失或类型错/文件不可读),诊断必须含出错路径
|
|
6
|
+
无效输入绝不允许被转换为貌似成功的裁决。
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
|
|
17
|
+
VALID = {
|
|
18
|
+
"id": "cli-demo",
|
|
19
|
+
"records": [
|
|
20
|
+
{"task_id": "t1", "harness": "h1", "model": "m1", "reward": 1.0, "reward_status": "verified"},
|
|
21
|
+
{"task_id": "t1", "harness": "h2", "model": "m1", "reward": 0.5, "reward_status": "verified"},
|
|
22
|
+
],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_cli(*args):
|
|
27
|
+
return subprocess.run([sys.executable, "-m", "agent_comparability", *args],
|
|
28
|
+
capture_output=True, text=True, cwd=ROOT)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _tmp_case(tmp_path, payload, raw=None, name="case.json"):
|
|
32
|
+
p = tmp_path / name
|
|
33
|
+
p.write_text(raw if raw is not None else json.dumps(payload), encoding="utf-8")
|
|
34
|
+
return str(p)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_valid_input_exit0_admissible(tmp_path):
|
|
38
|
+
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", "score_comparison")
|
|
39
|
+
assert r.returncode == 0, r.stderr
|
|
40
|
+
cdr = json.loads(r.stdout)
|
|
41
|
+
assert cdr["admissibility"] == "ADMISSIBLE"
|
|
42
|
+
assert cdr["policy_version"] == "cmp/0.4.0"
|
|
43
|
+
assert any("抗恶意篡改" in l for l in cdr["limitations"])
|
|
44
|
+
for k in ("case_id", "requested_claim", "satisfied_conditions", "violated_conditions",
|
|
45
|
+
"unknown_conditions", "supported_claims", "unsupported_claims",
|
|
46
|
+
"evidence_refs", "comparison_scope"):
|
|
47
|
+
assert k in cdr
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.parametrize("claim,expected", [
|
|
51
|
+
("causal", "INADMISSIBLE"),
|
|
52
|
+
("score_comparison", "ADMISSIBLE"),
|
|
53
|
+
])
|
|
54
|
+
def test_all_verdicts_are_exit0_not_program_failure(tmp_path, claim, expected):
|
|
55
|
+
"""INADMISSIBLE/PARTIAL/INCONCLUSIVE 与 ADMISSIBLE 同为 exit 0 —— 裁决 ≠ 程序失败。"""
|
|
56
|
+
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", claim)
|
|
57
|
+
assert r.returncode == 0, r.stderr
|
|
58
|
+
assert json.loads(r.stdout)["admissibility"] == expected
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_inconclusive_and_partial_exit0(tmp_path):
|
|
62
|
+
miss = {"id": "m", "records": [dict(VALID["records"][0], trajectory_available=False)]}
|
|
63
|
+
r = run_cli(_tmp_case(tmp_path, miss, name="miss.json"), "--claim", "score_comparison")
|
|
64
|
+
assert r.returncode == 0 and json.loads(r.stdout)["admissibility"] == "INCONCLUSIVE"
|
|
65
|
+
mixed = {"id": "x", "records": [
|
|
66
|
+
{"task_id": "t", "harness": "a", "model": "m", "reward": 0.0, "reward_status": "verified"},
|
|
67
|
+
{"task_id": "t", "harness": "b", "model": "m", "reward": 0.0, "reward_status": "unresolved_zero"}]}
|
|
68
|
+
r2 = run_cli(_tmp_case(tmp_path, mixed, name="mixed.json"), "--claim", "score_comparison")
|
|
69
|
+
assert r2.returncode == 0 and json.loads(r2.stdout)["admissibility"] == "PARTIAL"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_unknown_claim_type_exit0_inconclusive(tmp_path):
|
|
73
|
+
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", "leaderboard_ranking")
|
|
74
|
+
assert r.returncode == 0
|
|
75
|
+
cdr = json.loads(r.stdout)
|
|
76
|
+
assert cdr["admissibility"] == "INCONCLUSIVE"
|
|
77
|
+
assert any("unrecognized_claim_type" in u for u in cdr["unknown_conditions"])
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_unknown_fields_not_error_but_recorded(tmp_path):
|
|
81
|
+
"""SPEC §5:未知字段必须忽略(不报错)并记入 unknown_conditions —— CLI 不得比规范更严。"""
|
|
82
|
+
payload = json.loads(json.dumps(VALID))
|
|
83
|
+
payload["records"][0]["custom_extra"] = "keep-me-ignored"
|
|
84
|
+
r = run_cli(_tmp_case(tmp_path, payload), "--claim", "score_comparison")
|
|
85
|
+
assert r.returncode == 0, r.stderr
|
|
86
|
+
cdr = json.loads(r.stdout)
|
|
87
|
+
assert cdr["admissibility"] == "ADMISSIBLE"
|
|
88
|
+
assert any("ignored_fields" in u and "custom_extra" in u for u in cdr["unknown_conditions"])
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_output_file_flag(tmp_path):
|
|
92
|
+
out = tmp_path / "cdr.json"
|
|
93
|
+
r = run_cli(_tmp_case(tmp_path, VALID), "--claim", "score_comparison", "-o", str(out))
|
|
94
|
+
assert r.returncode == 0, r.stderr
|
|
95
|
+
assert r.stdout.strip() == ""
|
|
96
|
+
cdr = json.loads(out.read_text(encoding="utf-8"))
|
|
97
|
+
assert cdr["admissibility"] == "ADMISSIBLE"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ---------- 错误路径:全部 exit 2 + 诊断含路径 ----------
|
|
101
|
+
|
|
102
|
+
def test_missing_input_file_exit2(tmp_path):
|
|
103
|
+
r = run_cli(str(tmp_path / "nope.json"), "--claim", "score_comparison")
|
|
104
|
+
assert r.returncode == 2
|
|
105
|
+
assert "nope.json" in r.stderr
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_malformed_json_exit2(tmp_path):
|
|
109
|
+
r = run_cli(_tmp_case(tmp_path, None, raw="{not valid json"), "--claim", "score_comparison")
|
|
110
|
+
assert r.returncode == 2
|
|
111
|
+
assert "JSON" in r.stderr
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@pytest.mark.parametrize("payload,needle", [
|
|
115
|
+
({"records": VALID["records"]}, "id"), # 缺 id
|
|
116
|
+
({"id": "x"}, "records"), # 缺 records
|
|
117
|
+
({"id": "x", "records": []}, "records"), # 空 records
|
|
118
|
+
({"id": 42, "records": VALID["records"]}, "id"), # id 类型错
|
|
119
|
+
({"id": "x", "records": [{"harness": "h", "model": "m", "reward": 1.0,
|
|
120
|
+
"reward_status": "verified"}]}, "task_id"), # 缺必填字段
|
|
121
|
+
({"id": "x", "records": [dict(VALID["records"][0], reward="high")]}, "reward"), # reward 类型错
|
|
122
|
+
({"id": "x", "records": [dict(VALID["records"][0], reward=True)]}, "reward"), # bool 不是数值
|
|
123
|
+
({"id": "x", "records": [dict(VALID["records"][0], reward_status=0)]}, "reward_status"), # status 类型错
|
|
124
|
+
])
|
|
125
|
+
def test_contract_violations_exit2_with_path(tmp_path, payload, needle):
|
|
126
|
+
r = run_cli(_tmp_case(tmp_path, payload), "--claim", "score_comparison")
|
|
127
|
+
assert r.returncode == 2, f"期望 exit 2,实际 {r.returncode};stdout={r.stdout!r}"
|
|
128
|
+
assert needle in r.stderr, r.stderr
|
|
129
|
+
assert "records[0]" in r.stderr or needle == "records" or needle == "id" and "records" not in r.stderr
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_bad_optional_field_types_exit2(tmp_path):
|
|
133
|
+
rec = dict(VALID["records"][0], trajectory_available="yes")
|
|
134
|
+
r = run_cli(_tmp_case(tmp_path, {"id": "x", "records": [rec]}), "--claim", "score_comparison")
|
|
135
|
+
assert r.returncode == 2 and "trajectory_available" in r.stderr
|
|
136
|
+
rec2 = dict(VALID["records"][0], score_scale="0..1")
|
|
137
|
+
r2 = run_cli(_tmp_case(tmp_path, {"id": "x", "records": [rec2]}, name="c2.json"),
|
|
138
|
+
"--claim", "score_comparison")
|
|
139
|
+
assert r2.returncode == 2 and "score_scale" in r2.stderr
|
|
140
|
+
rec3 = dict(VALID["records"][0], repetition_count=0)
|
|
141
|
+
r3 = run_cli(_tmp_case(tmp_path, {"id": "x", "records": [rec3]}, name="c3.json"),
|
|
142
|
+
"--claim", "score_comparison")
|
|
143
|
+
assert r3.returncode == 2 and "repetition_count" in r3.stderr
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_missing_claim_flag_exit2(tmp_path):
|
|
147
|
+
r = run_cli(_tmp_case(tmp_path, VALID))
|
|
148
|
+
assert r.returncode == 2 # argparse required 参数缺失 → 2(argparse 惯例),不得默认猜 claim
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_no_args_exit2():
|
|
152
|
+
assert run_cli().returncode == 2
|