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
38 changes: 3 additions & 35 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,42 +51,10 @@ jobs:
python -m pip install -r requirements/ci-cpu.txt
python -m pip install --no-deps -e .
- name: Run dependency-light source contracts
run: python -m pytest -q tests/test_source_quality_contracts.py
run: python -m pytest -q tests/source
- name: Run offline CPU contracts
env:
CUDA_VISIBLE_DEVICES: ""
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
run: |
python -m pytest -q \
tests/test_capacity_handling.py \
tests/test_environment_scripts.py \
tests/test_onboarding.py \
tests/test_dependency_extras.py \
tests/test_cudnn_gqa_mode.py \
tests/test_dependency_audit.py \
tests/test_draft_model_config.py \
tests/test_draft_dsa.py \
tests/test_eagle4.py \
tests/test_eagle4_moe.py \
tests/test_eagle4_validation.py \
tests/test_fa4_import_compat.py \
tests/test_flex_attention_backend.py \
tests/test_dmtp.py \
tests/test_training_resume_rng.py \
tests/test_evaluation_contract.py \
tests/test_open_source_entrypoints.py \
tests/test_release_audit.py \
tests/test_shipped_config_defaults.py \
tests/test_release_recipes.py \
tests/test_recipe_node_loss.py \
tests/test_owner_fence.py \
tests/test_recipe_capacity.py \
tests/test_node_local_files.py \
tests/test_stage_recipe_inputs.py \
tests/test_target_lm_head.py \
tests/test_dspark_teacher_contract.py \
tests/test_planning.py \
tests/test_renderer_registry.py \
tests/test_reporting.py \
tests/test_training_config_validation.py
python -m pytest -q tests/test_wheel_assets.py tests/test_sglang_patch_contract.py
run: python -m pytest -q tests/cpu
13 changes: 12 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,18 @@ python -m tools.build_release --outdir /absolute/path/to/new-release-dist
Default tests are fast/offline. GPU, tokenizer, training and cuDNN gates need explicit switches
declared in their test files. A skip is not GPU acceptance. Public CI checks lint, local doc links,
compilation, plus a curated CPU/offline contract suite installed from `requirements/ci-cpu.txt`
on pull requests and configured branch pushes. The separate [release build](.github/workflows/build-release.yml)
on pull requests and configured branch pushes. CI uses pytest's directory discovery: put new
dependency-light repository checks in `tests/source/test_*.py` and offline CPU contracts supported by
the CI runtime in `tests/cpu/test_*.py`. New tests in either directory run automatically without
editing the workflow. Keep tests requiring GPUs, model downloads or the full pinned runtime outside
these directories. Run the same suites locally with:

```bash
python -m pytest -q tests/source
CUDA_VISIBLE_DEVICES="" HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 python -m pytest -q tests/cpu
```

The separate [release build](.github/workflows/build-release.yml)
workflow builds and audits sdist/wheel artifacts and checks wheel installation only when a `v*` tag
is pushed; see [release instructions](docs/releasing.md). Neither workflow certifies GPU health or
replaces the complete pinned-runtime suite above. Equivalent remote checks are not proof that hosted
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import tomllib
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
SHARED_CUTE_PACKAGES = ("nvidia-cutlass-dsl", "quack-kernels")


Expand Down
2 changes: 1 addition & 1 deletion tests/test_dmtp.py → tests/cpu/test_dmtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def test_conditioned_plan_rejects_unimplemented_combinations(key, value, monkeyp

monkeypatch.setenv("ASPEC_TRAIN_DATA", "examples/data/sample_conversations.jsonl")
monkeypatch.setenv("ASPEC_EVAL_DATA", "examples/data/sample_conversations.jsonl")
path = Path(__file__).parents[1] / "configs/recipes/qwen3_5_9b_dmtp.yaml"
path = Path(__file__).parents[2] / "configs/recipes/qwen3_5_9b_dmtp.yaml"
config = load_config(str(path), save_snapshot=False)
plan = compile_execution_plan(config)
assert plan.workload.objective["prediction_start"] == 1
Expand Down
4 changes: 2 additions & 2 deletions tests/test_draft_dsa.py → tests/cpu/test_draft_dsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def test_serving_export_does_not_silently_erase_dsa():


def test_dsa_example_configs_keep_same_identity_across_phase_boundaries():
root = Path(__file__).parents[1]
root = Path(__file__).parents[2]
configs = []
for stage, name in (("dense", "dense"), ("indexer_warmup", "indexer"), ("sparse", "sparse")):
path = root / f"aspec/config/dspark_qwen3_8b_dsa_{name}_draft_config.json"
Expand All @@ -372,7 +372,7 @@ def test_plan_preserves_dsa_and_packing_without_compression_alignment(monkeypatc
from aspec.config.train_config import load_config
from aspec.planning import compile_execution_plan

root = Path(__file__).parents[1]
root = Path(__file__).parents[2]
monkeypatch.chdir(root)
recipe = root / "configs/examples/qwen3_8b_dsa_dspark.yaml"
run = load_config(str(recipe), save_snapshot=False)
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/test_eagle4.py → tests/cpu/test_eagle4.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from aspec.training.checkpoint import load_initial_draft_weights
from tools import convert_to_hf

ROOT = Path(__file__).parents[1]
ROOT = Path(__file__).parents[2]


def raw_config(stage="draft", **changes):
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import pytest

ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]
WRAPPER = ROOT / "tools/run_in_env.sh"


Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/test_onboarding.py → tests/cpu/test_onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from aspec.planning import compile_execution_plan
from tools.quickstart import MODELS, build_profile

ROOT = Path(__file__).resolve().parents[1]
ROOT = Path(__file__).resolve().parents[2]


@pytest.mark.parametrize("name", MODELS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ def test_document_links_ignore_snapshots_and_fenced_examples(tmp_path):


def test_maintained_documentation_has_no_missing_local_targets():
assert not check_links(Path(__file__).resolve().parents[1])
assert not check_links(Path(__file__).resolve().parents[2])


def test_quickstart_has_two_gpu_roles_and_fixed_resume_horizon():
from aspec.config.train_config import load_config

root = Path(__file__).resolve().parents[1]
root = Path(__file__).resolve().parents[2]
config = load_config(str(root / "configs/recipes/quickstart_qwen3_8b.yaml"))
assert config.training.training_num_gpus_per_node == 1
assert config.inference.inference_num_gpus == 1
Expand All @@ -128,7 +128,7 @@ def test_quickstart_has_two_gpu_roles_and_fixed_resume_horizon():


def test_sdist_includes_runtime_install_requirements():
root = Path(__file__).resolve().parents[1]
root = Path(__file__).resolve().parents[2]
assert "recursive-include requirements *.txt" in (root / "MANIFEST.in").read_text()


Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/test_planning.py → tests/cpu/test_planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from aspec.planning import PlanCompileError, compile_execution_plan
from aspec.topology import current_capability_bundle

_REPO_ROOT = Path(__file__).parents[1]
_REPO_ROOT = Path(__file__).parents[2]
_REFERENCE_CONFIG = _REPO_ROOT / "configs" / "examples" / "sglang_qwen3_8b_dspark.yaml"
_DRAFT_CONFIG = _REPO_ROOT / "aspec" / "config" / "dspark_draft_config.json"

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


def _repo_root() -> Path:
return Path(__file__).parents[1]
return Path(__file__).parents[2]


def test_shipped_small_draft_configs_use_replicate():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_stage_and_verify_with_local_ssh_double(tmp_path):
"--host",
"fake-node",
]
root = Path(__file__).resolve().parents[1]
root = Path(__file__).resolve().parents[2]
subprocess.run(cmd, env=env, cwd=root, check=True, capture_output=True)
assert checksum_lines(source) == checksum_lines(destination)
assert subprocess.run(cmd, env=env, cwd=root, capture_output=True).returncode != 0
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import ast
from pathlib import Path

ROOT = Path(__file__).parents[1]
ROOT = Path(__file__).parents[2]
PUBLIC_TREES = (
".github",
"configs",
Expand Down Expand Up @@ -60,8 +60,8 @@
".yml",
}
HYGIENE_FIXTURES = {
ROOT / "tests/test_open_source_entrypoints.py",
ROOT / "tests/test_release_audit.py",
ROOT / "tests/cpu/test_open_source_entrypoints.py",
ROOT / "tests/cpu/test_release_audit.py",
Path(__file__),
}

Expand Down Expand Up @@ -228,5 +228,6 @@ def test_eagle4_validation_gates_do_not_use_optimizable_asserts():

def test_public_ci_includes_eagle4_cpu_contracts():
workflow = (ROOT / ".github/workflows/ci.yml").read_text(encoding="utf-8")
assert "python -m pytest -q tests/cpu" in workflow
for name in ("test_eagle4.py", "test_eagle4_moe.py", "test_eagle4_validation.py"):
assert f"tests/{name}" in workflow
assert (ROOT / "tests/cpu" / name).is_file()
20 changes: 10 additions & 10 deletions tools/recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,21 +192,21 @@ def main():
"-m",
"pytest",
"-q",
"tests/test_release_recipes.py",
"tests/test_node_local_files.py",
"tests/test_recipe_node_loss.py",
"tests/test_owner_fence.py",
"tests/test_recipe_capacity.py",
"tests/cpu/test_release_recipes.py",
"tests/cpu/test_node_local_files.py",
"tests/cpu/test_recipe_node_loss.py",
"tests/cpu/test_owner_fence.py",
"tests/cpu/test_recipe_capacity.py",
"tests/test_loop_cleanup.py",
"tests/test_stage_recipe_inputs.py",
"tests/cpu/test_stage_recipe_inputs.py",
"tests/test_packing.py",
"tests/test_direct_cp.py",
"tests/test_eagle3_trainer.py",
"tests/test_eagle4.py",
"tests/cpu/test_eagle4.py",
"tests/test_native_mtp.py",
"tests/test_dmtp.py",
"tests/test_training_resume_rng.py",
"tests/test_evaluation_contract.py",
"tests/cpu/test_dmtp.py",
"tests/cpu/test_training_resume_rng.py",
"tests/cpu/test_evaluation_contract.py",
"tests/test_ling_moe.py",
"tests/test_multi_draft_plan.py",
"tests/test_multinode_tcp_reference.py",
Expand Down
Loading