Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
done
printf 'shard %s/%s -> %s files\n' "$SHARD" "$SHARDS" "${#SEL[@]}"
if [ "${#SEL[@]}" -eq 0 ]; then echo "no files in shard"; exit 0; fi
uv run --frozen pytest -q --durations=25 \
uv run --frozen pytest -q --durations=25 -m "not od" \
--deselect=tests/challenge/test_capacity_outer_v2.py::test_actual_bounded_outer_child_matches_original_signed_replay \
--deselect=tests/challenge/test_service_capacity_v2.py::test_real_capacity_launch_failure_keeps_debit_and_prevents_restart_rerun \
"--deselect=tests/miner/test_capacity_runtime_v2.py::test_actual_small_kernel_attempt_and_cleanup[success]" \
Expand Down Expand Up @@ -113,4 +113,4 @@ jobs:
enable-cache: true
cache-dependency-glob: "uv.lock"
- run: uv sync --locked --all-extras --group dev
- run: uv run --frozen pytest -q --durations=25 tests/${{ matrix.suite }}
- run: uv run --frozen pytest -q --durations=25 -m "not od" tests/${{ matrix.suite }}
1 change: 0 additions & 1 deletion docs/schemas/RunManifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,6 @@
},
"preset": {
"enum": [
"od-tiny",
"od-base",
"od-large"
],
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ testpaths = ["tests"]
# the `pytest` console script does not put the rootdir on sys.path, `python -m pytest` does.
pythonpath = ["."]
addopts = "-ra --strict-markers"
markers = ["od: builds a real od-base OpenDecision model (excluded from PR CI)"]

[tool.ruff]
target-version = "py312"
Expand Down
5 changes: 2 additions & 3 deletions src/hypertrain/models/opendecision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ def check_manifest(m: RunManifest | RunManifestV2) -> None:
if ms.param_count != want:
raise ValueError(f"param_count {ms.param_count} != {want} trainable for {od.objective}")
n = PRESETS[od.preset].max_len
# A6 pins N = max_len (1024) for real presets; od-tiny (tests only) may use any N <= max_len.
tiny_ok = od.preset == "od-tiny" and ms.seq_len <= n
if od.objective == "mlm" and ms.seq_len != n and not tiny_ok:
# A6 pins N = max_len (1024).
if od.objective == "mlm" and ms.seq_len != n:
raise ValueError(f"mlm seq_len must be {n} for {od.preset} (A6)")


Expand Down
3 changes: 1 addition & 2 deletions src/hypertrain/protocol/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ def _omit_none(v: object) -> bool:

OD_PRESETS: dict[str, tuple[int, int, int, int, int]] = {
# preset: (d_model, n_layers, n_heads, head_layers, vocab)
"od-tiny": (64, 2, 4, 1, 259),
"od-base": (768, 12, 12, 2, 50432),
"od-large": (1024, 24, 16, 4, 50432),
}
Expand All @@ -70,7 +69,7 @@ def record_len(self) -> int:


class ODSpec(_M):
preset: Literal["od-tiny", "od-base", "od-large"]
preset: Literal["od-base", "od-large"]
head_layers: Pos
objective: Literal["mlm", "distill", "decision"]
mask_ratio: F32
Expand Down
5 changes: 1 addition & 4 deletions src/hypertrain/protocol/messages_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,7 @@ def _assign_unit_and_od(self) -> TrainingManifestV2:
raise ValueError("OD record required iff objective is not MLM")
if od.record is not None and ms.seq_len != od.record.record_len() - 1:
raise ValueError("OD record length must equal seq_len+1")
if od.objective == "mlm" and (
(od.preset == "od-tiny" and ms.seq_len > 64)
or (od.preset != "od-tiny" and ms.seq_len != 1024)
):
if od.objective == "mlm" and ms.seq_len != 1024:
raise ValueError("OD MLM sequence length differs from pinned preset")
if ds.sample_format != "u16[seq_len+1] token ids":
raise ValueError("OD requires u16 records")
Expand Down
4 changes: 1 addition & 3 deletions tests/auditor/test_island_bisect_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ def get(i: int):
(2, 1, None),
(2, 2, None),
(4, 2, None),
(2, 1, "mlm"),
(2, 1, "decision"),
(2, 1, "distill"),
*(pytest.param(2, 1, o, marks=pytest.mark.od) for o in ("mlm", "decision", "distill")),
],
)
def test_honest_traces_when_real_island_hooks(n: int, ep: int, objective: str | None) -> None:
Expand Down
6 changes: 5 additions & 1 deletion tests/auditor/test_island_replay_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ def fixture(objective: str | None = None, state_policy: str = "carry"):
return job, cache, blobs, get, result, key


@pytest.mark.parametrize("objective", [None, "mlm", "decision", "distill"])
@pytest.mark.parametrize(
"objective",
[None, *(pytest.param(o, marks=pytest.mark.od) for o in ("mlm", "decision", "distill"))],
)
def test_full_carry_and_window_equality(objective: str | None) -> None:
job, cache, blobs, get, expected, _ = fixture(objective)
outputs = emulate(2, lambda c: audit_island(job, c, get, *blobs, cache, now_round=2))
Expand Down Expand Up @@ -373,6 +376,7 @@ def test_missing_predecessor_reconstructs_once_under_two_h_budget() -> None:
)


@pytest.mark.od
def test_verified_anchor_restart_corruption_and_od_warm_start(tmp_path: Path) -> None:
from hypertrain.auditor.replay import tensor_root
from hypertrain.models.opendecision import extend_params
Expand Down
2 changes: 2 additions & 0 deletions tests/auditor/test_od_bisect.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from hypertrain.trainer.loop import train_round
from hypertrain.trainer.model import init_params

pytestmark = pytest.mark.od

N = 2
HON = Keypair(b"\x07" * 32).ss58
CHEAT = Keypair(b"\x08" * 32).ss58
Expand Down
2 changes: 2 additions & 0 deletions tests/challenge/test_authority_snapshot_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from hypertrain.protocol.hashing import sha256_hex
from hypertrain.protocol.jcs import canonicalize

pytestmark = pytest.mark.od

ROOT = Path(__file__).resolve().parents[2]
spec = importlib.util.spec_from_file_location(
"portable_authority_adapter", ROOT / "scripts/network_authority_snapshot.py"
Expand Down
2 changes: 2 additions & 0 deletions tests/challenge/test_carry_genesis_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from hypertrain.trainer.config import TrainConfig
from hypertrain.trainer.model import init_params

pytestmark = pytest.mark.od

spec = importlib.util.spec_from_file_location(
"carry_genesis_fixture", Path(__file__).parents[1] / "e2e/test_service_network_v2_e2e.py"
)
Expand Down
5 changes: 5 additions & 0 deletions tests/e2e/test_network_service_proof.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ def respond(request):
)


@pytest.mark.od
def test_decoder_probe_consumer_real_routes_receipt_and_debit_metadata(tmp_path, monkeypatch):
"""One real reference/proof transition; metadata oracle, not independent gradients."""
from types import SimpleNamespace
Expand Down Expand Up @@ -1328,6 +1329,7 @@ def test_endpoint_requires_explicit_https_without_ambient_credentials(tmp_path,
driver.Settings.model_validate(body)


@pytest.mark.od
def test_digest_authority_rejects_before_service_write(tmp_path, signed_package):
directory, _, _, _, _, _ = signed_package
cfg = settings(tmp_path).model_copy(update={"snapshot": directory})
Expand All @@ -1336,6 +1338,7 @@ def test_digest_authority_rejects_before_service_write(tmp_path, signed_package)
assert not cfg.evidence.exists()


@pytest.mark.od
def test_wrong_role_rejects_before_live_write(tmp_path, monkeypatch, signed_package):
directory, digest, roles, template, _, _ = signed_package
cfg = settings(tmp_path).model_copy(update={"snapshot": directory, "snapshot_digest": digest})
Expand Down Expand Up @@ -1409,6 +1412,7 @@ def forbidden(*args, **kwargs):
assert not (tmp_path / "none.log").exists()


@pytest.mark.od
def test_missing_service_adapter_rejects_before_evidence(tmp_path, monkeypatch, signed_package):
directory, digest, _, _, _, _ = signed_package
monkeypatch.setattr(driver, "durable_destination", lambda path: True)
Expand Down Expand Up @@ -1583,6 +1587,7 @@ def fail_copy(*args, **kwargs):
assert result["errors"] and not index["export_complete"]


@pytest.mark.od
@pytest.mark.parametrize("fault", ["offset", "beacon", "valid"])
def test_prepare_clock_probe_rejects_before_any_output(
tmp_path, monkeypatch, fault, signed_package
Expand Down
4 changes: 4 additions & 0 deletions tests/e2e/test_service_network_v2_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def carry_setup(manifest, policy, admission_policy, rows, tree):
yield from fixtures.network.__wrapped__(tmp_path, request)


@pytest.mark.od
@pytest.mark.parametrize("od_carry_network", ["mlm", "decision", "distill"], indirect=True)
def test_od_carry_admission_reference_requires_real_full_state(od_carry_network):
"""First production prerequisite: genuine corresponding-manifest graduation work."""
Expand Down Expand Up @@ -145,6 +146,7 @@ def test_actual_shadow_trial_does_not_mutate_production(network):
assert network.store._services(network.manifest.run_id())[0].balances().issued == 40000


@pytest.mark.od
@pytest.mark.parametrize("network", ["mlm", "decision", "distill"], indirect=True)
def test_od_two_rank_signed_service_reference_and_proof(network):
# All OD objectives use the real signed admission/reference/proof/finality routes,
Expand Down Expand Up @@ -1378,6 +1380,7 @@ def _graduate_four(network):
assert network.store._services(network.manifest.run_id())[0].balances().issued == 40000


@pytest.mark.od
@pytest.mark.parametrize("od_carry_network", ["mlm", "decision", "distill"], indirect=True)
def test_od_four_active_actual_client_two_round_carry_checkpoint(od_carry_network, monkeypatch):
"""Production service path with same-manifest admission and actual HTTP/TLS miners."""
Expand Down Expand Up @@ -1865,6 +1868,7 @@ async def now():
assert verify_network_checkpoint(checkpoint, fixtures.COORD.ss58) == []


@pytest.mark.od
@pytest.mark.parametrize("od_carry_network", ["mlm"], indirect=True)
def test_public_watch_layer_over_actual_fastapi_tls(od_carry_network, monkeypatch):
"""Funded original audits; real public CLI/TLS routes and committed long-poll wake."""
Expand Down
2 changes: 2 additions & 0 deletions tests/layout/test_od_island_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from hypertrain.trainer.loop import Assignment
from hypertrain.trainer.model import init_params

pytestmark = pytest.mark.od


def od_manifest(objective: str, n: int, zero1: bool, dtype: str = "fp32") -> RunManifestV2:
spec = importlib.util.spec_from_file_location(
Expand Down
1 change: 1 addition & 0 deletions tests/miner/test_island_launch_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,7 @@ def test_d2_signed_reservation_rejects_before_dispatch(tmp_path, fault):
assert len(lifecycle.j.all("network_plan_intake")) == (1 if fault == "replay" else 0)


@pytest.mark.od
@pytest.mark.parametrize("objective", ["mlm", "decision", "distill"])
def test_real_od_two_rank_worker_paths(tmp_path: Path, objective: str) -> None:
import importlib.util
Expand Down
2 changes: 2 additions & 0 deletions tests/models/test_opendecision.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
from hypertrain.trainer.loop import replay, train_round
from hypertrain.trainer.model import forward as dispatched_forward

pytestmark = pytest.mark.od

HEAD_PREFIXES = ("head.", "scorer.", "type_emb.", "unknown", "log_temp.")


Expand Down
2 changes: 2 additions & 0 deletions tests/protocol/test_network_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def commit(tokens: int = 7680) -> CommitV2:
)


@pytest.mark.od
@pytest.mark.parametrize("n", [1, 2, 4, 8])
@pytest.mark.parametrize("objective", ["mlm", "decision", "distill"])
@pytest.mark.parametrize("zero1", [False, True])
Expand All @@ -147,6 +148,7 @@ def test_od_general_n_when_v2(n: int, objective: str, zero1: bool) -> None:
RunManifest.model_validate(body)


@pytest.mark.od
def test_assign_unit_when_only_island_batch_is_divisible() -> None:
# Given
body = od_body()
Expand Down
26 changes: 13 additions & 13 deletions tests/protocol/test_od_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,20 @@ def od_body() -> dict[str, Any]:
b = copy.deepcopy(example_manifest().body())
b["model"] |= {
"arch": "od-encoder",
"n_layers": 2,
"d_model": 64,
"n_heads": 4,
"n_kv_heads": 4,
"d_ff": 256,
"vocab": 259,
"seq_len": 64,
"param_count": 187_140,
"n_layers": 12,
"d_model": 768,
"n_heads": 12,
"n_kv_heads": 12,
"d_ff": 3072,
"vocab": 50432,
"seq_len": 1024,
"param_count": 123_753_984,
"compute_dtype": "bf16",
"capacity_factor": f32hex(1.0),
"aux_loss_coef": f32hex(0.0),
"od": {
"preset": "od-tiny",
"head_layers": 1,
"preset": "od-base",
"head_layers": 2,
"objective": "mlm",
"mask_ratio": f32hex(0.3),
"mask_seed": 7,
Expand Down Expand Up @@ -121,8 +121,8 @@ def test_unit_root_without_unit_rejected() -> None:
lambda b: b["reference_spec"].update(profile="od-bf16-det-v1"),
lambda b: b["reference_spec"].pop("profile"),
lambda b: b["reference_spec"]["layout"].update(zero1=True),
lambda b: b["model"].update(vocab=260),
lambda b: b["model"]["od"].update(head_layers=2),
lambda b: b["model"].update(vocab=50433),
lambda b: b["model"]["od"].update(head_layers=1),
lambda b: b["model"]["od"].update(objective="decision"),
lambda b: b["model"]["od"].update(tokenizer_offset=4),
],
Expand All @@ -143,7 +143,7 @@ def test_decision_record_len_pins_seq_len() -> None:
b = od_body()
b["model"]["od"] |= {"objective": "decision", "record": rec}
b["model"]["seq_len"] = 1835
# d_model etc. still od-tiny; only seq_len/record coupling is under test
# d_model etc. still od-base; only seq_len/record coupling is under test
RunManifest.model_validate(b)
b["model"]["seq_len"] = 1834
with pytest.raises(ValidationError):
Expand Down
3 changes: 3 additions & 0 deletions tests/relay/test_transport_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ def command(*args, **kwargs):
assert outcome["provider_after"] is None and outcome["proof_status"] == "CENSORED"


@pytest.mark.od
def test_provider_fix_pinned_kind_version_and_fail_closed(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down Expand Up @@ -1140,6 +1141,7 @@ def test_provider_fix_pinned_kind_version_and_fail_closed(
module.require_capped_node(journal, "missing-marker")


@pytest.mark.od
def test_shim_raw_bytes_exit_and_pinned_kind_empty_preflight(tmp_path: Path) -> None:
import json
import os
Expand Down Expand Up @@ -1239,6 +1241,7 @@ def test_shim_raw_bytes_exit_and_pinned_kind_empty_preflight(tmp_path: Path) ->
assert not any(c[0] in {"run", "create", "start"} for c in calls)


@pytest.mark.od
@pytest.mark.parametrize("kernel_mode", ["valid", "wrong", "missing", "foreign", "forged"])
def test_pinned_kind_contract_through_capped_start(tmp_path: Path, kernel_mode: str) -> None:
import json
Expand Down
2 changes: 1 addition & 1 deletion tests/trainer/test_arch_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def od_cfg() -> TrainConfig:
def test_config_fills_od_fields(od_cfg: TrainConfig) -> None:
m = od_cfg.model
assert (m.arch, m.profile) == ("od-encoder", "od-bf16-det-eager-v1")
assert m.od is not None and m.od.preset == "od-tiny"
assert m.od is not None and m.od.preset == "od-base"
hash(m) # frozen ODSpec keeps ModelConfig hashable for the adapter cache


Expand Down
Loading