From 7eeb7c3f5cdc22ee55f5586eaef42208073b7563 Mon Sep 17 00:00:00 2001 From: Celve Date: Mon, 27 Jul 2026 19:27:41 +0800 Subject: [PATCH] test: remove tests directory --- .../benchmarks/test_geneval2_reproduction.py | 42 ----- tests/sde/test_prompt_seed.py | 10 -- tests/sft/test_agent_sft.py | 150 ------------------ tests/types/test_advantages_gae.py | 128 --------------- 4 files changed, 330 deletions(-) delete mode 100644 tests/benchmarks/test_geneval2_reproduction.py delete mode 100644 tests/sde/test_prompt_seed.py delete mode 100644 tests/sft/test_agent_sft.py delete mode 100644 tests/types/test_advantages_gae.py diff --git a/tests/benchmarks/test_geneval2_reproduction.py b/tests/benchmarks/test_geneval2_reproduction.py deleted file mode 100644 index f04b089ae..000000000 --- a/tests/benchmarks/test_geneval2_reproduction.py +++ /dev/null @@ -1,42 +0,0 @@ -import argparse - -import pytest - -from benchmarks.core.score import _geneval2_answer_token_ids -from benchmarks.run import ( - _parse_sim_even_batches, - _sim_even_metrics, - _sim_even_order, -) - - -class _Tokenizer: - def __init__(self) -> None: - self.calls = [] - - def encode(self, form, *, add_special_tokens): - self.calls.append((form, add_special_tokens)) - normalized = form.strip().lower() - return {"two": [2], "2": [2], "yes": [7]}.get(normalized, []) - - -def test_geneval2_answer_ids_exclude_special_tokens_and_deduplicate() -> None: - tokenizer = _Tokenizer() - - assert _geneval2_answer_token_ids(tokenizer, "How many cats?", "two") == [2] - assert tokenizer.calls - assert all(add_special_tokens is False for _, add_special_tokens in tokenizer.calls) - - -@pytest.mark.parametrize("value", ["0x32", "32x0", "32", "axb", "-1x2", "1x2x3"]) -def test_sim_even_batches_rejects_invalid_dimensions(value) -> None: - with pytest.raises(argparse.ArgumentTypeError): - _parse_sim_even_batches(value) - - -def test_sim_even_metrics_tolerates_failed_rows_and_fills_a_complete_wave() -> None: - rows = [{"score": 1.0}, None, {"score": 3.0}] - - assert _parse_sim_even_batches("2X2") == (2, 2) - assert _sim_even_order(3, world=2, batch_size=2) == [0, 1, 2, 0] - assert _sim_even_metrics(rows, ["score"], world=2, batch_size=2) == {"score_sim2x2": pytest.approx(5.0 / 3.0)} diff --git a/tests/sde/test_prompt_seed.py b/tests/sde/test_prompt_seed.py deleted file mode 100644 index c347d4a46..000000000 --- a/tests/sde/test_prompt_seed.py +++ /dev/null @@ -1,10 +0,0 @@ -from unirl.sde.noise import _derive_group_seed, make_prompt_seed_group_id - - -def test_prompt_seed_is_stable_per_sample_slot_and_distinct_across_siblings() -> None: - ids = [make_prompt_seed_group_id("same prompt", sample_ordinal=index) for index in range(3)] - seeds = [_derive_group_seed(42, group_id) for group_id in ids] - - assert len(set(seeds)) == 3 - assert seeds == [_derive_group_seed(42, group_id) for group_id in ids] - assert seeds[1] == (seeds[0] + 1) % (2**31) diff --git a/tests/sft/test_agent_sft.py b/tests/sft/test_agent_sft.py deleted file mode 100644 index 7c365e9b9..000000000 --- a/tests/sft/test_agent_sft.py +++ /dev/null @@ -1,150 +0,0 @@ -import json -import sys -from pathlib import Path -from types import ModuleType, SimpleNamespace - -import pytest -import torch -from omegaconf import OmegaConf - -from unirl.train.sft.track_builder import ARSupervisedTrackBuilder -from unirl.utils import prepare_sft_agent - - -class _Tokenizer: - eos_token_id = 99 - pad_token_id = 0 - - def __call__(self, text, *, add_special_tokens=False): - del text, add_special_tokens - return {"input_ids": [1, 2, 3]} - - def apply_chat_template( - self, - messages, - *, - tools=None, - add_generation_prompt, - enable_thinking, - tokenize, - return_dict, - truncation, - ): - del tools, enable_thinking, tokenize, return_dict, truncation - prompt_ids = [10, 11] - if add_generation_prompt: - return prompt_ids - target_length = int(messages[-1].get("_token_count", 2)) - return prompt_ids + [20] * (target_length - 1) + [self.eos_token_id] - - -class _ChatStage: - enable_thinking = False - - def embed(self, texts): - return texts - - -def _builder(*, append_eos: bool, max_response_length: int) -> ARSupervisedTrackBuilder: - bundle = SimpleNamespace(tokenizer=_Tokenizer(), device=torch.device("cpu")) - pipeline = SimpleNamespace(bundle=bundle, chat_template=_ChatStage()) - return ARSupervisedTrackBuilder( - pipeline=pipeline, - append_eos=append_eos, - max_response_length=max_response_length, - ) - - -def _agent_example(*, sample_id: str, target_length: int, tool_call: bool = False): - assistant = { - "role": "assistant", - "content": None if tool_call else "done", - "_token_count": target_length, - } - if tool_call: - assistant["tool_calls"] = [{"type": "function", "function": {"name": "lookup", "arguments": "{}"}}] - return { - "sample_id": sample_id, - "messages": [ - {"role": "user", "content": "help"}, - assistant, - ], - } - - -def test_overlong_tool_call_is_filtered_and_never_truncated() -> None: - long_tool_call = _agent_example(sample_id="long", target_length=5, tool_call=True) - short_answer = _agent_example(sample_id="short", target_length=2) - - kept, dropped = prepare_sft_agent._filter_overlong_targets( - [long_tool_call, short_answer], - tokenizer=_Tokenizer(), - max_response_length=4, - enable_thinking=False, - ) - - assert kept == [short_answer] - assert dropped == {"tool_call": 1, "final_answer": 0} - with pytest.raises(ValueError, match="filter overlong agent targets"): - _builder(append_eos=True, max_response_length=4)._tokenize_responses([long_tool_call]) - - -def test_append_eos_false_preserves_last_retained_token() -> None: - tokens, _ = _builder(append_eos=False, max_response_length=2)._tokenize_responses( - [{"sample_id": "legacy", "response": "three tokens"}] - ) - - assert tokens[0].tolist() == [1, 2] - - -def test_recipe_defaults_match_preparation_outputs(monkeypatch) -> None: - monkeypatch.delenv("SFT_DATA", raising=False) - monkeypatch.delenv("SFT_EVAL_DATA", raising=False) - repo_root = Path(__file__).resolve().parents[2] - cfg = OmegaConf.load(repo_root / "examples/sft/validation/qwen3_agent_sft_lora.yaml") - - assert cfg.data_source.manifest_path == "datasets/sft_agent_toolcall_12k/train.jsonl" - assert cfg.data_source.eval_manifest_path == "datasets/sft_agent_toolcall_12k/val.jsonl" - - -def test_prepare_keeps_train_and_validation_nonempty_for_high_val_fraction( - monkeypatch, - tmp_path, -) -> None: - source = tmp_path / "source.jsonl" - rows = [ - { - "messages": [ - {"role": "user", "content": f"question {index}"}, - {"role": "assistant", "content": "answer", "_token_count": 2}, - ] - } - for index in range(2) - ] - source.write_text("".join(json.dumps(row) + "\n" for row in rows)) - - hub = ModuleType("huggingface_hub") - hub.hf_hub_download = lambda **kwargs: str(source) - transformers = ModuleType("transformers") - transformers.AutoTokenizer = SimpleNamespace(from_pretrained=lambda _: _Tokenizer()) - monkeypatch.setitem(sys.modules, "huggingface_hub", hub) - monkeypatch.setitem(sys.modules, "transformers", transformers) - - out_dir = tmp_path / "prepared" - monkeypatch.setattr( - sys, - "argv", - [ - "prepare_sft_agent", - "--out-dir", - str(out_dir), - "--max-trajectories", - "2", - "--val-fraction", - "0.9", - ], - ) - prepare_sft_agent.main() - - assert len((out_dir / "train.jsonl").read_text().splitlines()) == 1 - assert len((out_dir / "val.jsonl").read_text().splitlines()) == 1 diff --git a/tests/types/test_advantages_gae.py b/tests/types/test_advantages_gae.py deleted file mode 100644 index 0f78bf9f8..000000000 --- a/tests/types/test_advantages_gae.py +++ /dev/null @@ -1,128 +0,0 @@ -"""CPU unit tests for GAE advantage computation.""" - -from __future__ import annotations - -import math - -import pytest -import torch - -from unirl.types.advantages import compute_gae_advantages - - -def test_gae_hand_computed_lambda_one() -> None: - """Sparse terminal reward; λ=1; hand-computed backward pass.""" - rewards = torch.tensor([0.0, 0.0, 1.0]) - values = torch.tensor([0.2, 0.5, 0.8]) - advantages, returns = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=1.0) - # δ = [0.3, 0.3, 0.2]; backward GAE with λ=1 → [0.8, 0.5, 0.2] - expected_adv = torch.tensor([0.8, 0.5, 0.2]) - assert torch.allclose(advantages, expected_adv, atol=1e-6) - assert torch.allclose(returns, expected_adv + values, atol=1e-6) - - -def test_gae_lambda_zero_td_only() -> None: - """λ=0 → advantages equal TD errors only.""" - rewards = torch.tensor([0.0, 0.0, 1.0]) - values = torch.tensor([0.2, 0.5, 0.8]) - advantages, _ = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=0.0) - expected = torch.tensor([0.3, 0.3, 0.2]) - assert torch.allclose(advantages, expected, atol=1e-6) - - -def test_gae_flat_critic_sparse_reward() -> None: - """Perfect flat critic on sparse terminal reward → zero advantages.""" - rewards = torch.tensor([0.0, 0.0, 0.0, 1.0]) - values = torch.tensor([1.0, 1.0, 1.0, 1.0]) - advantages, _ = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=1.0) - assert torch.allclose(advantages, torch.zeros(4), atol=1e-6) - - -def test_gae_uniform_reward_zero_advantage() -> None: - """Constant rewards and matching finite-horizon values → zero TD errors.""" - rewards = torch.tensor([1.0, 1.0, 1.0]) - values = torch.tensor([3.0, 2.0, 1.0]) - advantages, _ = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=0.95) - assert torch.allclose(advantages, torch.zeros(3), atol=1e-6) - - -def test_gae_batched_independent_rows() -> None: - """Batched [B, T] matches row-wise 1D computation.""" - rewards = torch.tensor([[0.0, 1.0], [1.0, 0.0]]) - values = torch.tensor([[0.1, 0.4], [0.2, 0.5]]) - batched_adv, _ = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=1.0) - for b in range(2): - row_adv, _ = compute_gae_advantages(rewards[b], values[b], gamma=1.0, gae_lambda=1.0) - assert torch.allclose(batched_adv[b], row_adv, atol=1e-6) - - -def test_gae_mask_resets_carry() -> None: - """Padding mask prevents GAE from crossing sequence boundaries.""" - rewards = torch.tensor([[0.0, 0.0, 1.0, 0.0]]) - values = torch.tensor([[0.2, 0.5, 0.8, 0.0]]) - mask = torch.tensor([[1.0, 1.0, 1.0, 0.0]]) - advantages, _ = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=1.0, mask=mask) - unmasked_adv, _ = compute_gae_advantages(rewards[0, :3], values[0, :3], gamma=1.0, gae_lambda=1.0) - assert torch.allclose(advantages[0, :3], unmasked_adv, atol=1e-6) - assert advantages[0, 3].item() == 0.0 - - -@pytest.mark.parametrize("batched", [False, True]) -def test_gae_mask_zeros_bootstrap_from_padding_values(batched: bool) -> None: - """The last valid step is terminal even when padding contains nonzero values.""" - rewards = torch.tensor([0.0, 0.0, 0.0]) - values = torch.tensor([0.25, 123.0, 456.0]) - mask = torch.tensor([1.0, 0.0, 0.0]) - if batched: - rewards = rewards.unsqueeze(0) - values = values.unsqueeze(0) - mask = mask.unsqueeze(0) - - advantages, _ = compute_gae_advantages( - rewards, - values, - gamma=1.0, - gae_lambda=1.0, - mask=mask, - ) - - expected = torch.tensor([-0.25, 0.0, 0.0]) - if batched: - expected = expected.unsqueeze(0) - assert torch.allclose(advantages, expected, atol=1e-6) - - -def test_gae_gamma_discount() -> None: - """γ < 1 discounts bootstrap from future values.""" - rewards = torch.tensor([0.0, 0.0, 1.0]) - values = torch.tensor([0.0, 0.0, 0.0]) - gamma = 0.9 - advantages, _ = compute_gae_advantages(rewards, values, gamma=gamma, gae_lambda=1.0) - # δ2=1, δ1=0, δ0=0 → backward: A2=1, A1=0.9, A0=0.81 - expected = torch.tensor([gamma**2, gamma, 1.0]) - assert torch.allclose(advantages, expected, atol=1e-6) - - -def test_gae_shape_mismatch_raises() -> None: - rewards = torch.tensor([0.0, 1.0]) - values = torch.tensor([0.0, 1.0, 0.0]) - with pytest.raises(ValueError, match="shape"): - compute_gae_advantages(rewards, values) - - -def test_gae_invalid_hyperparams_raise() -> None: - rewards = torch.tensor([1.0]) - values = torch.tensor([0.5]) - with pytest.raises(ValueError, match="gamma"): - compute_gae_advantages(rewards, values, gamma=1.1) - with pytest.raises(ValueError, match="gae_lambda"): - compute_gae_advantages(rewards, values, gae_lambda=-0.1) - - -def test_gae_single_step() -> None: - """Degenerate T=1: advantage = r + γ·0 - V.""" - rewards = torch.tensor([1.0]) - values = torch.tensor([0.25]) - advantages, returns = compute_gae_advantages(rewards, values, gamma=1.0, gae_lambda=0.95) - assert math.isclose(float(advantages.item()), 0.75, rel_tol=0, abs_tol=1e-6) - assert math.isclose(float(returns.item()), 1.0, rel_tol=0, abs_tol=1e-6)