mirror of
https://github.com/deepseek-ai/DeepInfer.git
synced 2026-10-29 23:37:28 +08:00
Merge 2afdb3d2d8ed1fb3a83a0169485aa25d281196e8 into 553741557a57df44a46fb3ab72b6aafeaf945337
This commit is contained in:
commit
c9dba916a2
258
tests/test_config.py
Normal file
258
tests/test_config.py
Normal file
@ -0,0 +1,258 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
"""Unit tests for configuration classes: CacheConfig, ParallelConfig, SchedulerConfig.
|
||||
|
||||
These tests cover:
|
||||
- CacheConfig: gpu_memory_utilization bounds (upper + lower), block_size
|
||||
storage, deferred fields.
|
||||
- ParallelConfig: _verify_args assertions, data_parallel_size auto-derive
|
||||
when 0 is passed, world_size computation.
|
||||
- SchedulerConfig: max_num_batched_tokens vs. max_model_len check,
|
||||
prefill interval lower bounds.
|
||||
|
||||
No model weights, GPU, or CUDA device are required; all tests run on CPU.
|
||||
|
||||
NOTE: test_cache_config_rejects_zero_utilization and
|
||||
test_cache_config_rejects_negative_utilization are currently marked xfail
|
||||
because the lower-bound guard has not yet been added to CacheConfig._verify_args.
|
||||
They will automatically flip to XPASS (and then be un-marked) once the companion
|
||||
fix `fix/cache-config-validation` is merged.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from deepinfer.config import CacheConfig, ParallelConfig, SchedulerConfig
|
||||
from deepinfer.engine.engine_type import EngineType
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CacheConfig
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCacheConfig:
|
||||
def test_valid_construction(self) -> None:
|
||||
cfg = CacheConfig(block_size=16, gpu_memory_utilization=0.9)
|
||||
assert cfg.block_size == 16
|
||||
assert cfg.gpu_memory_utilization == pytest.approx(0.9)
|
||||
|
||||
def test_boundary_utilization_one(self) -> None:
|
||||
"""Exactly 1.0 must be accepted (full GPU allocation)."""
|
||||
cfg = CacheConfig(block_size=16, gpu_memory_utilization=1.0)
|
||||
assert cfg.gpu_memory_utilization == pytest.approx(1.0)
|
||||
|
||||
def test_lookahead_tokens_default_zero(self) -> None:
|
||||
cfg = CacheConfig(block_size=16, gpu_memory_utilization=0.5)
|
||||
assert cfg.lookahead_tokens == 0
|
||||
|
||||
def test_lookahead_tokens_stored(self) -> None:
|
||||
cfg = CacheConfig(block_size=16, gpu_memory_utilization=0.5, lookahead_tokens=4)
|
||||
assert cfg.lookahead_tokens == 4
|
||||
|
||||
def test_deferred_fields_initially_none(self) -> None:
|
||||
cfg = CacheConfig(block_size=16, gpu_memory_utilization=0.5)
|
||||
assert cfg.num_gpu_blocks is None
|
||||
assert cfg.num_state_seqs is None
|
||||
assert cfg.bytes_of_state_per_seqs is None
|
||||
assert cfg.bytes_per_block is None
|
||||
|
||||
def test_above_one_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="GPU memory utilization"):
|
||||
CacheConfig(block_size=16, gpu_memory_utilization=1.01)
|
||||
|
||||
def test_well_above_one_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="GPU memory utilization"):
|
||||
CacheConfig(block_size=16, gpu_memory_utilization=2.0)
|
||||
|
||||
@pytest.mark.xfail(
|
||||
strict=True,
|
||||
reason="Lower-bound guard missing in CacheConfig._verify_args; "
|
||||
"fixed by PR fix/cache-config-validation.",
|
||||
)
|
||||
def test_zero_utilization_raises(self) -> None:
|
||||
"""gpu_memory_utilization=0.0 should be rejected — reserving zero GPU
|
||||
memory for KV blocks makes the engine non-functional and produces a
|
||||
cryptic division-by-zero deep in the memory profiler."""
|
||||
with pytest.raises(ValueError, match="GPU memory utilization"):
|
||||
CacheConfig(block_size=16, gpu_memory_utilization=0.0)
|
||||
|
||||
@pytest.mark.xfail(
|
||||
strict=True,
|
||||
reason="Lower-bound guard missing in CacheConfig._verify_args; "
|
||||
"fixed by PR fix/cache-config-validation.",
|
||||
)
|
||||
def test_negative_utilization_raises(self) -> None:
|
||||
"""Negative gpu_memory_utilization is nonsensical and should be rejected
|
||||
at construction time rather than surfacing as a cryptic failure later."""
|
||||
with pytest.raises(ValueError, match="GPU memory utilization"):
|
||||
CacheConfig(block_size=16, gpu_memory_utilization=-0.5)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ParallelConfig
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _parallel(
|
||||
dp: int = 2,
|
||||
cp: int = 1,
|
||||
ep: int = 2,
|
||||
engram: int = 0,
|
||||
engine_rank: int = 0,
|
||||
engine_group_rank: int = 0,
|
||||
engine_type: EngineType = EngineType.NORMAL,
|
||||
) -> ParallelConfig:
|
||||
return ParallelConfig(
|
||||
data_parallel_size=dp,
|
||||
context_parallel_size=cp,
|
||||
expert_parallel_size=ep,
|
||||
engram_parallel_size=engram,
|
||||
engine_rank=engine_rank,
|
||||
engine_group_rank=engine_group_rank,
|
||||
engine_type=engine_type,
|
||||
)
|
||||
|
||||
|
||||
class TestParallelConfig:
|
||||
def test_valid_minimal(self) -> None:
|
||||
cfg = _parallel(dp=2, cp=1, ep=2, engram=0)
|
||||
assert cfg.data_parallel_size == 2
|
||||
assert cfg.context_parallel_size == 1
|
||||
assert cfg.expert_parallel_size == 2
|
||||
|
||||
def test_data_parallel_auto_derive_from_ep_and_cp(self) -> None:
|
||||
"""When data_parallel_size=0, it should be derived as ep // cp."""
|
||||
cfg = _parallel(dp=0, cp=2, ep=4, engram=0)
|
||||
assert cfg.data_parallel_size == 2 # 4 // 2
|
||||
|
||||
def test_data_parallel_auto_derive_minimum_one(self) -> None:
|
||||
"""Auto-derived data_parallel_size is always at least 1."""
|
||||
# ep=1, cp=4 → integer division gives 0, but clamped to 1.
|
||||
cfg = _parallel(dp=0, cp=4, ep=1, engram=0, engine_rank=0)
|
||||
assert cfg.data_parallel_size == 1
|
||||
|
||||
def test_world_size_computed_correctly(self) -> None:
|
||||
# world_size = max(cp * dp, engram_parallel_size)
|
||||
cfg = _parallel(dp=4, cp=2, ep=4, engram=0)
|
||||
# engram defaults to model_parallel_size * dp = 2 * 4 = 8
|
||||
assert cfg.world_size == 8
|
||||
|
||||
def test_data_parallel_rank(self) -> None:
|
||||
cfg = _parallel(dp=4, cp=1, ep=4, engram=0, engine_rank=3)
|
||||
assert cfg.data_parallel_rank == 3 % 4
|
||||
|
||||
def test_model_parallel_size_equals_context_parallel(self) -> None:
|
||||
cfg = _parallel(dp=2, cp=3, ep=6, engram=0)
|
||||
assert cfg.model_parallel_size == 3
|
||||
|
||||
def test_engram_defaults_to_model_times_dp(self) -> None:
|
||||
cfg = _parallel(dp=2, cp=2, ep=4, engram=0)
|
||||
# engram_parallel_size = model_parallel_size * dp = 2 * 2 = 4
|
||||
assert cfg.engram_parallel_size == 4
|
||||
|
||||
def test_invalid_ep_not_divisible_raises(self) -> None:
|
||||
"""(cp * dp) must be divisible by ep."""
|
||||
with pytest.raises(AssertionError):
|
||||
_parallel(dp=2, cp=2, ep=3, engram=0) # 2*2=4 not divisible by 3
|
||||
|
||||
def test_engine_rank_out_of_range_raises(self) -> None:
|
||||
"""engine_rank must be in [0, world_size // model_parallel_size)."""
|
||||
with pytest.raises(AssertionError):
|
||||
_parallel(dp=2, cp=1, ep=2, engram=0, engine_rank=999)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SchedulerConfig
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestSchedulerConfig:
|
||||
def test_valid_basic(self) -> None:
|
||||
cfg = SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=64,
|
||||
max_model_len=4096,
|
||||
)
|
||||
assert cfg.max_num_seqs == 64
|
||||
assert cfg.max_model_len == 4096
|
||||
|
||||
def test_max_num_batched_tokens_defaults_to_max_model_len_or_4096(self) -> None:
|
||||
cfg = SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=4,
|
||||
max_model_len=2048,
|
||||
)
|
||||
# _get_max_num_batched_tokens returns max(max_model_len, 4096, decode_tokens)
|
||||
assert cfg.max_num_batched_tokens >= 4096
|
||||
|
||||
def test_explicit_max_num_batched_tokens(self) -> None:
|
||||
cfg = SchedulerConfig(
|
||||
max_num_batched_tokens=8192,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
)
|
||||
assert cfg.max_num_batched_tokens == 8192
|
||||
|
||||
def test_too_small_batched_tokens_raises(self) -> None:
|
||||
"""max_num_batched_tokens < max_model_len must be rejected when
|
||||
chunked_prefill is disabled, because it would silently cap the
|
||||
effective context window."""
|
||||
with pytest.raises(ValueError, match="max_num_batched_tokens"):
|
||||
SchedulerConfig(
|
||||
max_num_batched_tokens=512,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
chunked_prefill=False,
|
||||
)
|
||||
|
||||
def test_too_small_batched_tokens_ok_with_chunked_prefill(self) -> None:
|
||||
"""With chunked_prefill=True the constraint is lifted."""
|
||||
SchedulerConfig(
|
||||
max_num_batched_tokens=512,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
chunked_prefill=True,
|
||||
)
|
||||
|
||||
def test_prefill_attempt_interval_zero_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="prefill_attempt_interval"):
|
||||
SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
prefill_attempt_interval=0,
|
||||
)
|
||||
|
||||
def test_prefill_attempt_interval_negative_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="prefill_attempt_interval"):
|
||||
SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
prefill_attempt_interval=-1,
|
||||
)
|
||||
|
||||
def test_prefill_any_attempt_interval_zero_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="prefill_any_attempt_interval"):
|
||||
SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
prefill_any_attempt_interval=0,
|
||||
)
|
||||
|
||||
def test_runtime_max_model_len_greater_than_max_model_len(self) -> None:
|
||||
cfg = SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
)
|
||||
assert cfg.runtime_max_model_len >= cfg.max_model_len
|
||||
|
||||
def test_stop_preempted_request_stored(self) -> None:
|
||||
cfg = SchedulerConfig(
|
||||
max_num_batched_tokens=None,
|
||||
max_num_seqs=4,
|
||||
max_model_len=4096,
|
||||
stop_preempted_request=True,
|
||||
)
|
||||
assert cfg.stop_preempted_request is True
|
||||
323
tests/test_sampling_params.py
Normal file
323
tests/test_sampling_params.py
Normal file
@ -0,0 +1,323 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
"""Unit tests for SamplingParams validation and construction.
|
||||
|
||||
These tests exercise every validation rule in SamplingParams._verify_args(),
|
||||
the greedy-mode clamping applied after validation, the _make_seed helper,
|
||||
and the deprecation warning for the `logprobs` parameter.
|
||||
|
||||
All tests are CPU-only and require no model weights or CUDA device.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from deepinfer.sampling_params import SAMPLING_EPS, SamplingParams
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def make(**kwargs) -> SamplingParams:
|
||||
"""Construct SamplingParams with defaults overridden by kwargs."""
|
||||
return SamplingParams(**kwargs)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Default construction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestDefaults:
|
||||
def test_default_construction_succeeds(self) -> None:
|
||||
sp = SamplingParams()
|
||||
assert sp.temperature == pytest.approx(1.0)
|
||||
assert sp.top_p == pytest.approx(1.0)
|
||||
assert sp.top_k == -1
|
||||
assert sp.ignore_eos is False
|
||||
assert sp.max_tokens == 16
|
||||
assert sp.prompt_logprobs is False
|
||||
assert sp.top_logprobs == 0
|
||||
assert sp.so_args is None
|
||||
assert sp.golden_token_ids is None
|
||||
|
||||
def test_seq_seed_auto_generated(self) -> None:
|
||||
sp1 = SamplingParams()
|
||||
sp2 = SamplingParams()
|
||||
# With overwhelming probability two auto-seeds differ.
|
||||
# If they collide the test is still valid (just flaky by chance).
|
||||
assert 0 <= sp1.seq_seed < 2**64
|
||||
assert 0 <= sp2.seq_seed < 2**64
|
||||
|
||||
def test_repr_contains_key_fields(self) -> None:
|
||||
sp = SamplingParams(temperature=0.8, max_tokens=64)
|
||||
r = repr(sp)
|
||||
assert "temperature=0.8" in r
|
||||
assert "max_tokens=64" in r
|
||||
assert "top_p=" in r
|
||||
assert "seq_seed=" in r
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# temperature
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestTemperature:
|
||||
def test_valid_positive(self) -> None:
|
||||
make(temperature=0.7)
|
||||
|
||||
def test_valid_exactly_one(self) -> None:
|
||||
make(temperature=1.0)
|
||||
|
||||
def test_valid_high(self) -> None:
|
||||
make(temperature=2.5)
|
||||
|
||||
def test_zero_triggers_greedy_clamp(self) -> None:
|
||||
"""temperature=0 should not raise; instead it is clamped to SAMPLING_EPS
|
||||
and top_p/top_k are reset to greedy defaults."""
|
||||
sp = make(temperature=0.0, top_p=0.9, top_k=50)
|
||||
assert sp.temperature == pytest.approx(SAMPLING_EPS)
|
||||
assert sp.top_p == pytest.approx(1.0)
|
||||
assert sp.top_k == -1
|
||||
|
||||
def test_near_zero_below_eps_triggers_greedy_clamp(self) -> None:
|
||||
sp = make(temperature=SAMPLING_EPS / 2)
|
||||
assert sp.temperature == pytest.approx(SAMPLING_EPS)
|
||||
|
||||
def test_exactly_eps_is_not_clamped(self) -> None:
|
||||
sp = make(temperature=SAMPLING_EPS)
|
||||
# SAMPLING_EPS is NOT strictly less than SAMPLING_EPS, so no clamp.
|
||||
assert sp.temperature == pytest.approx(SAMPLING_EPS)
|
||||
|
||||
def test_negative_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="temperature must be non-negative"):
|
||||
make(temperature=-0.1)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# top_p
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestTopP:
|
||||
def test_valid_one(self) -> None:
|
||||
make(top_p=1.0)
|
||||
|
||||
def test_valid_fraction(self) -> None:
|
||||
make(top_p=0.9)
|
||||
|
||||
def test_valid_near_zero(self) -> None:
|
||||
make(top_p=1e-9)
|
||||
|
||||
def test_zero_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_p must be in"):
|
||||
make(top_p=0.0)
|
||||
|
||||
def test_above_one_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_p must be in"):
|
||||
make(top_p=1.01)
|
||||
|
||||
def test_negative_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_p must be in"):
|
||||
make(top_p=-0.5)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# top_k
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestTopK:
|
||||
def test_disabled(self) -> None:
|
||||
make(top_k=-1)
|
||||
|
||||
def test_valid_one(self) -> None:
|
||||
make(top_k=1)
|
||||
|
||||
def test_valid_large(self) -> None:
|
||||
make(top_k=torch.iinfo(torch.int32).max)
|
||||
|
||||
def test_zero_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_k must be -1"):
|
||||
make(top_k=0)
|
||||
|
||||
def test_negative_other_than_minus_one_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_k must be -1"):
|
||||
make(top_k=-2)
|
||||
|
||||
def test_above_int32_max_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_k must be -1"):
|
||||
make(top_k=torch.iinfo(torch.int32).max + 1)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# max_tokens
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestMaxTokens:
|
||||
def test_valid_minimum(self) -> None:
|
||||
sp = make(max_tokens=1)
|
||||
assert sp.max_tokens == 1
|
||||
|
||||
def test_valid_default(self) -> None:
|
||||
sp = make()
|
||||
assert sp.max_tokens == 16
|
||||
|
||||
def test_valid_large(self) -> None:
|
||||
make(max_tokens=131072)
|
||||
|
||||
def test_zero_raises(self) -> None:
|
||||
"""max_tokens=0 must raise ValueError rather than being silently coerced to 1."""
|
||||
with pytest.raises(ValueError, match="max_tokens must be >= 1"):
|
||||
make(max_tokens=0)
|
||||
|
||||
def test_negative_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="max_tokens must be >= 1"):
|
||||
make(max_tokens=-1)
|
||||
|
||||
def test_above_int32_max_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="max_tokens must be >= 1"):
|
||||
make(max_tokens=torch.iinfo(torch.int32).max + 1)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# top_logprobs
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestTopLogprobs:
|
||||
def test_valid_zero(self) -> None:
|
||||
make(top_logprobs=0)
|
||||
|
||||
def test_valid_boundary(self) -> None:
|
||||
make(top_logprobs=1000)
|
||||
|
||||
def test_negative_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_logprobs must be in"):
|
||||
make(top_logprobs=-1)
|
||||
|
||||
def test_above_1000_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="top_logprobs must be in"):
|
||||
make(top_logprobs=1001)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# seq_seed
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestSeqSeed:
|
||||
def test_none_generates_random_seed(self) -> None:
|
||||
sp = make(seq_seed=None)
|
||||
assert 0 <= sp.seq_seed < 2**64
|
||||
|
||||
def test_explicit_seed_stored(self) -> None:
|
||||
sp = make(seq_seed=42)
|
||||
assert sp.seq_seed == 42
|
||||
|
||||
def test_seed_clamped_to_uint64(self) -> None:
|
||||
# A value larger than uint64 is masked to the lower 64 bits.
|
||||
big = (2**64) + 5
|
||||
sp = make(seq_seed=big)
|
||||
assert sp.seq_seed == 5
|
||||
|
||||
def test_zero_seed(self) -> None:
|
||||
sp = make(seq_seed=0)
|
||||
assert sp.seq_seed == 0
|
||||
|
||||
def test_max_uint64_seed(self) -> None:
|
||||
max_seed = 2**64 - 1
|
||||
sp = make(seq_seed=max_seed)
|
||||
assert sp.seq_seed == max_seed
|
||||
|
||||
def test_non_int_seed_raises(self) -> None:
|
||||
with pytest.raises(TypeError):
|
||||
make(seq_seed="not-an-int") # type: ignore[arg-type]
|
||||
|
||||
def test_make_seed_deterministic_for_same_value(self) -> None:
|
||||
assert SamplingParams._make_seed(123) == SamplingParams._make_seed(123)
|
||||
|
||||
def test_make_seed_none_is_random(self) -> None:
|
||||
random.seed(0)
|
||||
s1 = SamplingParams._make_seed(None)
|
||||
random.seed(0)
|
||||
s2 = SamplingParams._make_seed(None)
|
||||
# Same random state → same result.
|
||||
assert s1 == s2
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# logprobs deprecation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestLogprobsDeprecation:
|
||||
def test_logprobs_false_no_warning(self) -> None:
|
||||
"""The default logprobs=False must not emit any warning."""
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
make(logprobs=False)
|
||||
|
||||
def test_logprobs_true_emits_deprecation_warning(self) -> None:
|
||||
with pytest.warns(DeprecationWarning, match="logprobs.*deprecated"):
|
||||
make(logprobs=True)
|
||||
|
||||
def test_logprobs_true_does_not_affect_top_logprobs(self) -> None:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
sp = make(logprobs=True, top_logprobs=5)
|
||||
assert sp.top_logprobs == 5
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Optional / passthrough fields
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestOptionalFields:
|
||||
def test_golden_token_ids_stored(self) -> None:
|
||||
ids = [1, 2, 3]
|
||||
sp = make(golden_token_ids=ids)
|
||||
assert sp.golden_token_ids == ids
|
||||
|
||||
def test_golden_token_ids_none_by_default(self) -> None:
|
||||
assert make().golden_token_ids is None
|
||||
|
||||
def test_so_args_stored(self) -> None:
|
||||
so = {"schema_type": "json", "schema": "{}"}
|
||||
sp = make(so_args=so)
|
||||
assert sp.so_args == so
|
||||
|
||||
def test_ignore_eos_stored(self) -> None:
|
||||
sp = make(ignore_eos=True)
|
||||
assert sp.ignore_eos is True
|
||||
|
||||
def test_first_n_token_no_eos_stored(self) -> None:
|
||||
sp = make(first_n_token_no_eos=3, eos_id=2)
|
||||
assert sp.first_n_token_no_eos == 3
|
||||
assert sp.eos_id == 2
|
||||
|
||||
def test_prompt_logprobs_stored(self) -> None:
|
||||
sp = make(prompt_logprobs=True)
|
||||
assert sp.prompt_logprobs is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Greedy mode interaction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestGreedyMode:
|
||||
def test_greedy_overrides_top_k(self) -> None:
|
||||
sp = make(temperature=0.0, top_k=100)
|
||||
assert sp.top_k == -1
|
||||
|
||||
def test_non_greedy_preserves_top_k(self) -> None:
|
||||
sp = make(temperature=0.5, top_k=50)
|
||||
assert sp.top_k == 50
|
||||
|
||||
def test_greedy_overrides_top_p(self) -> None:
|
||||
sp = make(temperature=0.0, top_p=0.95)
|
||||
assert sp.top_p == pytest.approx(1.0)
|
||||
|
||||
def test_non_greedy_preserves_top_p(self) -> None:
|
||||
sp = make(temperature=0.5, top_p=0.95)
|
||||
assert sp.top_p == pytest.approx(0.95)
|
||||
Loading…
x
Reference in New Issue
Block a user