adduce 0.1.0__py3-none-any.whl
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.
- adduce/__init__.py +3 -0
- adduce/cache.py +44 -0
- adduce/checklists/__init__.py +149 -0
- adduce/checklists/acl.yaml +44 -0
- adduce/checklists/neurips.yaml +44 -0
- adduce/cli.py +715 -0
- adduce/config.py +54 -0
- adduce/dynamic/__init__.py +7 -0
- adduce/dynamic/import_hook.py +111 -0
- adduce/dynamic/reproduce.py +169 -0
- adduce/dynamic/resolve.py +98 -0
- adduce/engine.py +134 -0
- adduce/evidence/__init__.py +117 -0
- adduce/evidence/config.py +141 -0
- adduce/evidence/data.py +127 -0
- adduce/evidence/dependencies.py +227 -0
- adduce/evidence/docs.py +116 -0
- adduce/evidence/environment.py +75 -0
- adduce/evidence/git.py +32 -0
- adduce/evidence/latex.py +280 -0
- adduce/evidence/notebook.py +127 -0
- adduce/evidence/portability.py +73 -0
- adduce/evidence/precision.py +118 -0
- adduce/evidence/python_ast.py +611 -0
- adduce/evidence/remote.py +179 -0
- adduce/evidence/results.py +146 -0
- adduce/evidence/run_history.py +179 -0
- adduce/fixers/__init__.py +157 -0
- adduce/fixers/codemods/__init__.py +1 -0
- adduce/fixers/codemods/pin_revision.py +75 -0
- adduce/fixers/templates/CITATION.cff.j2 +13 -0
- adduce/fixers/templates/Dockerfile.j2 +19 -0
- adduce/fixers/templates/readme_sections.md.j2 +37 -0
- adduce/fixers/templates/reproduce.sh.j2 +17 -0
- adduce/fixers/templates/seed_utils.py.j2 +59 -0
- adduce/graph.py +164 -0
- adduce/llm/__init__.py +119 -0
- adduce/manifest.py +256 -0
- adduce/manifest_builder.py +132 -0
- adduce/model.py +233 -0
- adduce/modes.py +111 -0
- adduce/naming/__init__.py +139 -0
- adduce/profiles/__init__.py +82 -0
- adduce/profiles/acl.toml +21 -0
- adduce/profiles/acm.toml +21 -0
- adduce/profiles/default.toml +21 -0
- adduce/profiles/iclr.toml +21 -0
- adduce/profiles/neurips.toml +21 -0
- adduce/profiles/strict.toml +21 -0
- adduce/report/__init__.py +34 -0
- adduce/report/appendix.py +101 -0
- adduce/report/badge.py +70 -0
- adduce/report/checksums.py +38 -0
- adduce/report/codemeta.py +29 -0
- adduce/report/croissant.py +55 -0
- adduce/report/json_report.py +48 -0
- adduce/report/latex.py +58 -0
- adduce/report/markdown.py +61 -0
- adduce/report/ro_crate.py +69 -0
- adduce/report/sarif.py +91 -0
- adduce/report/software_heritage.py +36 -0
- adduce/report/terminal.py +212 -0
- adduce/report/zenodo.py +24 -0
- adduce/reviewer_time.py +90 -0
- adduce/rules/__init__.py +4 -0
- adduce/rules/archival.py +97 -0
- adduce/rules/base.py +135 -0
- adduce/rules/builtin.py +5 -0
- adduce/rules/checkpoint.py +130 -0
- adduce/rules/data.py +245 -0
- adduce/rules/deps.py +262 -0
- adduce/rules/determinism.py +321 -0
- adduce/rules/docs.py +128 -0
- adduce/rules/drift.py +334 -0
- adduce/rules/env.py +198 -0
- adduce/rules/exec_.py +140 -0
- adduce/rules/licensing.py +91 -0
- adduce/rules/notebook.py +220 -0
- adduce/rules/portability.py +108 -0
- adduce/rules/precision.py +180 -0
- adduce/rules/reconcile.py +197 -0
- adduce/rules/registry.py +218 -0
- adduce/rules/remote.py +165 -0
- adduce/rules/run.py +150 -0
- adduce/rules/versioning.py +77 -0
- adduce/scoring.py +136 -0
- adduce-0.1.0.dist-info/METADATA +301 -0
- adduce-0.1.0.dist-info/RECORD +91 -0
- adduce-0.1.0.dist-info/WHEEL +4 -0
- adduce-0.1.0.dist-info/entry_points.txt +5 -0
- adduce-0.1.0.dist-info/licenses/LICENSE +21 -0
adduce/__init__.py
ADDED
adduce/cache.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Local cache under ``.adduce/cache`` for opt-in online resolutions.
|
|
2
|
+
|
|
3
|
+
Nothing in the default offline path reads or writes this cache. Online
|
|
4
|
+
commands (``pin-remotes``, ``--online``) store resolved public metadata
|
|
5
|
+
(Hugging Face revisions, GitHub SHAs, URL headers) here so repeated runs do
|
|
6
|
+
not re-query, and so the resolved values are inspectable.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
CACHE_DIR = ".adduce/cache"
|
|
17
|
+
_DEFAULT_TTL_SECONDS = 7 * 24 * 3600
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Cache:
|
|
21
|
+
def __init__(self, root: Path, ttl_seconds: int = _DEFAULT_TTL_SECONDS) -> None:
|
|
22
|
+
self.directory = root / CACHE_DIR
|
|
23
|
+
self.ttl_seconds = ttl_seconds
|
|
24
|
+
|
|
25
|
+
def _path_for(self, key: str) -> Path:
|
|
26
|
+
safe = "".join(c if c.isalnum() or c in "-._" else "_" for c in key)
|
|
27
|
+
return self.directory / f"{safe}.json"
|
|
28
|
+
|
|
29
|
+
def get(self, key: str) -> Any | None:
|
|
30
|
+
target = self._path_for(key)
|
|
31
|
+
if not target.is_file():
|
|
32
|
+
return None
|
|
33
|
+
try:
|
|
34
|
+
entry = json.loads(target.read_text(encoding="utf-8"))
|
|
35
|
+
except (json.JSONDecodeError, OSError):
|
|
36
|
+
return None
|
|
37
|
+
if time.time() - entry.get("stored_at", 0) > self.ttl_seconds:
|
|
38
|
+
return None
|
|
39
|
+
return entry.get("value")
|
|
40
|
+
|
|
41
|
+
def put(self, key: str, value: Any) -> None:
|
|
42
|
+
self.directory.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
entry = {"stored_at": time.time(), "value": value}
|
|
44
|
+
self._path_for(key).write_text(json.dumps(entry, indent=2), encoding="utf-8")
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Conference checklist drafting from repository evidence.
|
|
2
|
+
|
|
3
|
+
Each bundled checklist maps its items to rule IDs. Items are answered from
|
|
4
|
+
finding statuses (all pass → Yes, mixed → Partial, all fail → No); items the
|
|
5
|
+
repository cannot answer are marked for the authors explicitly. The output
|
|
6
|
+
is a draft: honest wording about that is part of the design, not a
|
|
7
|
+
disclaimer bolted on.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from importlib import resources
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import yaml
|
|
17
|
+
|
|
18
|
+
from ..engine import CheckResult
|
|
19
|
+
from ..rules.base import Finding, Status
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ChecklistItem:
|
|
24
|
+
id: str
|
|
25
|
+
question: str
|
|
26
|
+
rules: list[str] = field(default_factory=list)
|
|
27
|
+
manual: bool = False
|
|
28
|
+
guidance: str = ""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Checklist:
|
|
33
|
+
name: str
|
|
34
|
+
key: str
|
|
35
|
+
preamble: str
|
|
36
|
+
items: list[ChecklistItem]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def available_checklists() -> list[str]:
|
|
40
|
+
return sorted(
|
|
41
|
+
entry.name[: -len(".yaml")]
|
|
42
|
+
for entry in resources.files(__package__).iterdir()
|
|
43
|
+
if entry.name.endswith(".yaml")
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def load_checklist(name_or_path: str) -> Checklist:
|
|
48
|
+
path = Path(name_or_path)
|
|
49
|
+
if path.suffix in {".yaml", ".yml"} and path.is_file():
|
|
50
|
+
text = path.read_text(encoding="utf-8")
|
|
51
|
+
else:
|
|
52
|
+
resource = resources.files(__package__).joinpath(f"{name_or_path}.yaml")
|
|
53
|
+
if not resource.is_file():
|
|
54
|
+
raise ValueError(
|
|
55
|
+
f"Unknown checklist '{name_or_path}'. Bundled: {', '.join(available_checklists())}."
|
|
56
|
+
)
|
|
57
|
+
text = resource.read_text(encoding="utf-8")
|
|
58
|
+
data = yaml.safe_load(text)
|
|
59
|
+
items = [
|
|
60
|
+
ChecklistItem(
|
|
61
|
+
id=item["id"],
|
|
62
|
+
question=item["question"],
|
|
63
|
+
rules=list(item.get("rules", [])),
|
|
64
|
+
manual=bool(item.get("manual", False)),
|
|
65
|
+
guidance=str(item.get("guidance", "")).strip(),
|
|
66
|
+
)
|
|
67
|
+
for item in data.get("items", [])
|
|
68
|
+
]
|
|
69
|
+
return Checklist(
|
|
70
|
+
name=data.get("name", name_or_path),
|
|
71
|
+
key=data.get("key", name_or_path),
|
|
72
|
+
preamble=str(data.get("preamble", "")).strip(),
|
|
73
|
+
items=items,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _draft_answer(findings: list[Finding]) -> tuple[str, list[str]]:
|
|
78
|
+
"""Derive a draft answer and its supporting evidence lines."""
|
|
79
|
+
scored = [f for f in findings if f.status.score_value is not None]
|
|
80
|
+
evidence = []
|
|
81
|
+
for finding in findings:
|
|
82
|
+
marker = {Status.PASS: "found", Status.PARTIAL: "partial", Status.FAIL: "missing"}.get(
|
|
83
|
+
finding.status, "n/a"
|
|
84
|
+
)
|
|
85
|
+
evidence.append(f"[{marker}] {finding.rule_id}: {finding.message}")
|
|
86
|
+
if not scored:
|
|
87
|
+
return "No repository evidence", evidence
|
|
88
|
+
values = [f.status.score_value for f in scored]
|
|
89
|
+
if all(v == 1.0 for v in values):
|
|
90
|
+
return "Yes (draft)", evidence
|
|
91
|
+
if all(v == 0.0 for v in values):
|
|
92
|
+
return "No (draft)", evidence
|
|
93
|
+
return "Partial (draft)", evidence
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def render_markdown(
|
|
97
|
+
checklist: Checklist,
|
|
98
|
+
result: CheckResult,
|
|
99
|
+
llm_drafts: dict[str, str] | None = None,
|
|
100
|
+
) -> str:
|
|
101
|
+
"""Render the filled checklist. ``llm_drafts`` optionally carries
|
|
102
|
+
LLM-phrased justification prose keyed by item id; the yes/no evidence
|
|
103
|
+
answers stay deterministic regardless."""
|
|
104
|
+
llm_drafts = llm_drafts or {}
|
|
105
|
+
findings_by_rule = {f.rule_id: f for f in result.card.findings}
|
|
106
|
+
lines: list[str] = []
|
|
107
|
+
lines.append(f"# {checklist.name}")
|
|
108
|
+
lines.append("")
|
|
109
|
+
lines.append(f"Repository: `{result.repo.root.name}`"
|
|
110
|
+
+ (f" at commit `{(result.repo.git.head_commit or '')[:7]}`" if result.repo.git.head_commit else ""))
|
|
111
|
+
lines.append("")
|
|
112
|
+
if checklist.preamble:
|
|
113
|
+
lines.append(f"> {checklist.preamble}")
|
|
114
|
+
lines.append("")
|
|
115
|
+
|
|
116
|
+
for index, item in enumerate(checklist.items, start=1):
|
|
117
|
+
lines.append(f"## {index}. {item.question}")
|
|
118
|
+
lines.append("")
|
|
119
|
+
item_findings = [findings_by_rule[r] for r in item.rules if r in findings_by_rule]
|
|
120
|
+
if item.manual:
|
|
121
|
+
lines.append("**Answer:** _requires author input — not derivable from the repository._")
|
|
122
|
+
else:
|
|
123
|
+
answer, _ = _draft_answer(item_findings)
|
|
124
|
+
lines.append(f"**Answer:** {answer}")
|
|
125
|
+
lines.append("")
|
|
126
|
+
if item.id in llm_drafts:
|
|
127
|
+
lines.append("**Draft justification** (LLM-phrased from the evidence below — verify):")
|
|
128
|
+
lines.append("")
|
|
129
|
+
lines.append(llm_drafts[item.id])
|
|
130
|
+
lines.append("")
|
|
131
|
+
if item_findings:
|
|
132
|
+
lines.append("**Repository evidence:**")
|
|
133
|
+
lines.append("")
|
|
134
|
+
_, evidence = _draft_answer(item_findings)
|
|
135
|
+
for line in evidence:
|
|
136
|
+
lines.append(f"- {line}")
|
|
137
|
+
lines.append("")
|
|
138
|
+
if item.guidance:
|
|
139
|
+
lines.append(f"_{item.guidance}_")
|
|
140
|
+
lines.append("")
|
|
141
|
+
|
|
142
|
+
lines.append("---")
|
|
143
|
+
lines.append("")
|
|
144
|
+
lines.append(
|
|
145
|
+
"Drafted from static repository evidence. Verify each answer against the "
|
|
146
|
+
"paper before submission; answers about the paper text cannot be derived here."
|
|
147
|
+
)
|
|
148
|
+
lines.append("")
|
|
149
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: "ACL Rolling Review Responsible Research Checklist (artifact items)"
|
|
2
|
+
key: acl
|
|
3
|
+
preamble: >
|
|
4
|
+
Draft answers derived from repository evidence. Complete and verify every
|
|
5
|
+
answer before submission; several items depend on the paper text and on
|
|
6
|
+
assets the tool cannot inspect.
|
|
7
|
+
items:
|
|
8
|
+
- id: artifacts-released
|
|
9
|
+
question: "Did you release the code and models used in your work, with sufficient documentation to use them?"
|
|
10
|
+
rules: [R-EXEC-002, R-DOC-001, R-LIC-001, R-VER-001]
|
|
11
|
+
guidance: >
|
|
12
|
+
Reviewers expect a repository with install and usage documentation and
|
|
13
|
+
an explicit license.
|
|
14
|
+
- id: artifacts-cited
|
|
15
|
+
question: "Did you cite the creators of the artifacts (datasets, models, code) you used?"
|
|
16
|
+
rules: [R-LIC-002, R-LIC-003]
|
|
17
|
+
manual: true
|
|
18
|
+
guidance: >
|
|
19
|
+
Citation of third-party artifacts lives in the paper; the repository
|
|
20
|
+
side is a citation section, CITATION.cff, and stated asset licenses.
|
|
21
|
+
- id: reproducibility-parameters
|
|
22
|
+
question: "Did you report the number of parameters, total computational budget, and computing infrastructure used?"
|
|
23
|
+
rules: [R-DOC-001, R-PREC-005, R-RUN-003]
|
|
24
|
+
guidance: >
|
|
25
|
+
Add a hardware-and-runtime note to the README and mirror it in the
|
|
26
|
+
paper; SLURM scripts' resource requests should match.
|
|
27
|
+
- id: experimental-setup
|
|
28
|
+
question: "Did you discuss the experimental setup, including hyperparameter search and best-found hyperparameter values?"
|
|
29
|
+
rules: [R-DOC-002, R-DRIFT-001, R-DRIFT-002]
|
|
30
|
+
guidance: >
|
|
31
|
+
Committed config files per reported experiment are the strongest
|
|
32
|
+
supporting evidence; drift findings mean paper and configs disagree.
|
|
33
|
+
- id: descriptive-statistics
|
|
34
|
+
question: "Did you report descriptive statistics about your results (e.g., error bars, summary statistics over multiple runs)?"
|
|
35
|
+
rules: [R-RES-003]
|
|
36
|
+
guidance: >
|
|
37
|
+
Multi-seed run scripts and variance reporting are the repository-side
|
|
38
|
+
evidence.
|
|
39
|
+
- id: data-documentation
|
|
40
|
+
question: "Did you document the data you used or created, including how it can be obtained?"
|
|
41
|
+
rules: [R-DATA-001, R-DATA-002, R-DATA-003, R-DATA-005]
|
|
42
|
+
guidance: >
|
|
43
|
+
A download script or dataset DOI plus checksums makes data access
|
|
44
|
+
mechanical and verifiable.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: "NeurIPS Paper Checklist (reproducibility items)"
|
|
2
|
+
key: neurips
|
|
3
|
+
preamble: >
|
|
4
|
+
Draft answers derived from repository evidence. Every answer must be
|
|
5
|
+
reviewed and completed by the authors before submission: the tool sees the
|
|
6
|
+
repository, not the paper, and several checklist items depend on claims
|
|
7
|
+
made in the paper text.
|
|
8
|
+
items:
|
|
9
|
+
- id: experimental-reproducibility
|
|
10
|
+
question: "Does the paper fully disclose all the information needed to reproduce the main experimental results?"
|
|
11
|
+
rules: [R-DOC-001, R-DOC-002, R-DOC-003, R-DET-001, R-EXEC-003]
|
|
12
|
+
guidance: >
|
|
13
|
+
Reviewers look for the experimental setup, hyperparameters, seeds, and
|
|
14
|
+
the mapping from commands to reported numbers.
|
|
15
|
+
- id: open-access-code-data
|
|
16
|
+
question: "Does the paper provide open access to the data and code, with sufficient instructions to faithfully reproduce the main experimental results?"
|
|
17
|
+
rules: [R-EXEC-002, R-ENV-001, R-DATA-001, R-DATA-002, R-DOC-001]
|
|
18
|
+
guidance: >
|
|
19
|
+
An anonymised repository URL with exact run commands and environment
|
|
20
|
+
specification is the expected supporting material.
|
|
21
|
+
- id: experimental-details
|
|
22
|
+
question: "Does the paper specify all the training and test details (data splits, hyperparameters, how they were chosen, optimizer)?"
|
|
23
|
+
rules: [R-DOC-002, R-DRIFT-001, R-DRIFT-003, R-DATA-001]
|
|
24
|
+
guidance: >
|
|
25
|
+
Point to the committed config files for each reported experiment; drift
|
|
26
|
+
findings indicate paper and configs disagree.
|
|
27
|
+
- id: error-bars
|
|
28
|
+
question: "Does the paper report error bars or other information about the statistical significance of the experiments?"
|
|
29
|
+
rules: [R-RES-003]
|
|
30
|
+
guidance: >
|
|
31
|
+
Multi-seed run scripts and variance reporting are the repository-side
|
|
32
|
+
evidence; the statistical claims themselves live in the paper.
|
|
33
|
+
- id: compute-resources
|
|
34
|
+
question: "Does the paper provide sufficient information on the computer resources (type of compute workers, memory, time of execution) needed to reproduce the experiments?"
|
|
35
|
+
rules: [R-DOC-001, R-PREC-005, R-RUN-003, R-DRIFT-005]
|
|
36
|
+
guidance: >
|
|
37
|
+
State GPU/CPU model, count, memory, and approximate wall-clock time per
|
|
38
|
+
experiment; precision settings (TF32/AMP) belong here too.
|
|
39
|
+
- id: code-of-ethics-license
|
|
40
|
+
question: "Are the creators or original owners of assets (code, data, models) properly credited, and are the license and terms of use explicitly mentioned and respected?"
|
|
41
|
+
rules: [R-LIC-001, R-LIC-002, R-LIC-003]
|
|
42
|
+
guidance: >
|
|
43
|
+
License the released code, provide citation metadata, and state the
|
|
44
|
+
licenses of datasets and pretrained assets you build on.
|