Spaces:
Sleeping
Sleeping
Download tests/test_engine.py from spitfire4794/test1111111: direct link, hf CLI and curl.
- Browser
- Download file 30.4 kB
-
https://huggingface.co/spaces/spitfire4794/test1111111/resolve/main/tests/test_engine.py
- Command line
-
hf download hf://spaces/spitfire4794/test1111111/tests/test_engine.py
-
curl -L -o test_engine.py https://huggingface.co/spaces/spitfire4794/test1111111/resolve/main/tests/test_engine.py
30.4 kB
| """SDK contracts, deterministic streaming edge cases, and real native integration.""" | |
| from concurrent.futures import ThreadPoolExecutor | |
| import threading | |
| import time | |
| import pytest | |
| from cism import Engine, GenerationChunk, GenerationResult | |
| class FakeTokenizer: | |
| bos_token_id = 0 | |
| eos_token_id = None | |
| chat_template = "test template" | |
| def __init__(self, pieces=None, encoded=None, special=()): | |
| self.pieces = pieces or {} | |
| self.encoded = [1, 2] if encoded is None else encoded | |
| self.special = set(special) | |
| self.encode_calls = [] | |
| self.chat_calls = [] | |
| def encode(self, text, *, add_special_tokens): | |
| self.encode_calls.append((text, add_special_tokens)) | |
| return self.encoded.copy() | |
| def decode(self, tokens, *, skip_special_tokens, clean_up_tokenization_spaces): | |
| assert skip_special_tokens is True | |
| assert clean_up_tokenization_spaces is False | |
| return b"".join( | |
| self.pieces.get(token, f"<{token}>".encode()) | |
| for token in tokens if token not in self.special | |
| ).decode("utf-8", errors="replace") | |
| def apply_chat_template(self, messages, *, tokenize, add_generation_prompt): | |
| self.chat_calls.append((messages, tokenize, add_generation_prompt)) | |
| return self.encoded.copy() | |
| class FakeSession: | |
| def __init__(self, output, options): | |
| self.output = list(output) | |
| self.options = options | |
| self.generated_tokens = 0 | |
| self.finish_reason = "length" if options["max_new_tokens"] == 0 else "" | |
| self.cancelled = threading.Event() | |
| self.requests = [] | |
| def next_tokens(self, count, spec_k=0): | |
| self.requests.append((count, spec_k)) | |
| batch = [] | |
| for _ in range(count): | |
| if self.finish_reason: | |
| break | |
| assert self.generated_tokens < len(self.output), "Fake script exhausted" | |
| token = self.output[self.generated_tokens] | |
| self.generated_tokens += 1 | |
| batch.append(token) | |
| if token in self.options["eos_token_ids"]: | |
| self.finish_reason = "stop" | |
| elif self.generated_tokens == self.options["max_new_tokens"]: | |
| self.finish_reason = "length" | |
| return batch | |
| def cancel(self): | |
| self.cancelled.set() | |
| if not self.finish_reason: | |
| self.finish_reason = "cancelled" | |
| class FakeNative: | |
| info = {"precision": "fp32", "weight_bytes": 123, "threads": 1} | |
| def __init__(self, output): | |
| self.output = output | |
| self.sessions = [] | |
| self.prompts = [] | |
| self.logits_calls = [] | |
| self.touches = 0 | |
| self.scans = 0 | |
| self.pages_locked = False | |
| def create_session(self, prompt, **options): | |
| self.prompts.append(prompt.copy()) | |
| session = FakeSession(self.output, options) | |
| self.sessions.append(session) | |
| return session | |
| def logits(self, prompt): | |
| self.logits_calls.append(prompt) | |
| return [0.25, 0.75] | |
| def touch(self): | |
| self.touches += 1 | |
| return 123 | |
| def scan(self): | |
| self.scans += 1 | |
| return 123 | |
| def lock_pages(self): | |
| self.pages_locked = True | |
| return 123 | |
| def unlock_pages(self): | |
| self.pages_locked = False | |
| return 123 | |
| def make_engine(output=(3, 4, 5, 6), *, pieces=None, tokenizer=None, **config): | |
| native = FakeNative(output) | |
| tokenizer = tokenizer or FakeTokenizer(pieces) | |
| engine = Engine(native, tokenizer, { | |
| "model_type": "llama", "vocab_size": 64, "max_position_embeddings": 256, | |
| **config, | |
| }, "fake-model", revision="pinned") | |
| return engine, native | |
| def assert_released(engine, native): | |
| assert engine._generation_lock.acquire(blocking=False), "Generation lock leaked" | |
| engine._generation_lock.release() | |
| assert all(session.cancelled.is_set() for session in native.sessions) | |
| def test_encode_chat_and_metadata(): | |
| engine, native = make_engine() | |
| prompt = [4, 5] | |
| assert engine.encode(prompt) == prompt | |
| assert engine.encode(prompt) is not prompt | |
| assert engine.encode("hello") == [1, 2] | |
| assert engine.tokenizer.encode_calls == [("hello", True)] | |
| messages = [{"role": "user", "content": "hello"}] | |
| assert engine.format_chat(messages) == [1, 2] | |
| assert engine.tokenizer.chat_calls == [(messages, True, True)] | |
| assert engine.logits(prompt) == [0.25, 0.75] | |
| assert native.logits_calls == [prompt] | |
| assert engine.info == { | |
| **native.info, "model": "fake-model", "revision": "pinned", | |
| "context_length": 256, "architecture": "llama", | |
| "capabilities": ["text", "streaming", "sampling"], | |
| "keep_warm": False, | |
| } | |
| engine.info["precision"] = "changed" | |
| assert native.info["precision"] == "fp32" | |
| engine.tokenizer.chat_template = None | |
| with pytest.raises(ValueError, match="chat template"): | |
| engine.format_chat(messages) | |
| def test_empty_prompt_bos_and_usage(prompt): | |
| engine, native = make_engine(tokenizer=FakeTokenizer(encoded=[]), bos_token_id=7) | |
| result = engine.generate(prompt, max_new_tokens=1) | |
| assert native.prompts == [[7]] | |
| assert result.prompt_tokens == 1 | |
| del engine.config["bos_token_id"] | |
| assert engine.encode(prompt) == [0] | |
| engine.tokenizer.bos_token_id = None | |
| with pytest.raises(ValueError, match="BOS"): | |
| engine.encode(prompt) | |
| def test_invalid_prompt_tokens(prompt): | |
| engine, native = make_engine() | |
| with pytest.raises(ValueError, match="Token IDs"): | |
| engine.generate(prompt, max_new_tokens=1) | |
| assert not native.sessions | |
| assert_released(engine, native) | |
| def test_invalid_prompt_type(prompt): | |
| engine, _ = make_engine() | |
| with pytest.raises(TypeError, match="prompt"): | |
| engine.encode(prompt) | |
| def test_invalid_eos_ids(eos): | |
| with pytest.raises(ValueError, match="Token IDs"): | |
| make_engine(eos_token_id=eos) | |
| def test_invalid_options_before_native_call(options): | |
| engine, native = make_engine() | |
| with pytest.raises(ValueError): | |
| list(engine.stream([1], **{"max_new_tokens": 1, **options})) | |
| assert not native.sessions | |
| assert_released(engine, native) | |
| def test_stream_generate_consistency_and_sampling_forwarding(chunk_size): | |
| engine, native = make_engine(pieces={3: b"a", 4: b" b", 5: b"c", 6: b"!"}) | |
| options = dict(max_new_tokens=4, temperature=0.7, top_p=0.8, top_k=9, | |
| seed=2**64 - 1, chunk_size=chunk_size) | |
| chunks = list(engine.stream("prompt", **options)) | |
| assert all(isinstance(chunk, GenerationChunk) for chunk in chunks) | |
| assert chunks[0].token_ids == [3] | |
| assert chunks[-1].finish_reason == "length" | |
| assert all(chunk.finish_reason is None for chunk in chunks[:-1]) | |
| total = 0 | |
| for chunk in chunks: | |
| total += len(chunk.token_ids) | |
| assert chunk.completion_tokens == total | |
| assert chunk.prompt_tokens == 2 | |
| result = engine.generate("prompt", **options) | |
| assert result == GenerationResult("a bc!", [3, 4, 5, 6], "length", 2, 4) | |
| assert result.text == "".join(chunk.text for chunk in chunks) | |
| assert result.token_ids == [token for chunk in chunks for token in chunk.token_ids] | |
| assert native.sessions[0].options == { | |
| "max_new_tokens": 4, "temperature": 0.7, "top_p": 0.8, | |
| "top_k": 9, "seed": 2**64 - 1, "eos_token_ids": [], | |
| } | |
| assert native.sessions[0].requests == [(1, 0)] + [(chunk_size, 0)] * (len(chunks) - 1) | |
| assert_released(engine, native) | |
| def test_stop_sequences(pieces, stops, text, count, chunk_size): | |
| ids = list(range(3, 3 + len(pieces))) | |
| engine, native = make_engine(ids, pieces=dict(zip(ids, pieces))) | |
| chunks = list(engine.stream([1, 2], max_new_tokens=len(ids), stop=stops, | |
| chunk_size=chunk_size)) | |
| expected_reason = "stop" if any(stop in b"".join(pieces).decode() for stop in stops) else "length" | |
| assert "".join(chunk.text for chunk in chunks) == text | |
| assert all(text.startswith("".join(chunk.text for chunk in chunks[:i + 1])) | |
| for i in range(len(chunks))) | |
| assert chunks[-1].finish_reason == expected_reason | |
| assert chunks[-1].completion_tokens == count | |
| assert [token for chunk in chunks for token in chunk.token_ids] == ids[:count] | |
| assert native.sessions[0].generated_tokens == count | |
| assert native.sessions[0].requests == [(1, 0)] * count | |
| assert engine.generate([1, 2], max_new_tokens=len(ids), stop=stops, | |
| chunk_size=chunk_size) == GenerationResult( | |
| text, ids[:count], expected_reason, 2, count) | |
| assert_released(engine, native) | |
| def test_partial_utf8_is_held_until_complete(chunk_size): | |
| engine, native = make_engine(pieces={3: b"x\xe2", 4: b"\x82", 5: b"\xac", 6: b"!"}) | |
| chunks = list(engine.stream([1], max_new_tokens=4, chunk_size=chunk_size)) | |
| assert chunks[0].text == "x" | |
| assert all("\ufffd" not in chunk.text for chunk in chunks) | |
| assert "".join(chunk.text for chunk in chunks) == "x\u20ac!" | |
| assert_released(engine, native) | |
| def test_partial_utf8_at_length_is_flushed(): | |
| engine, _ = make_engine((3,), pieces={3: b"x\xe2"}) | |
| assert engine.generate([1], max_new_tokens=1).text == "x\ufffd" | |
| def test_replacement_stop_is_honored_at_terminal_decode(pieces): | |
| engine, _ = make_engine((3,), pieces={3: pieces}) | |
| assert engine.generate([1], max_new_tokens=1, stop=["\ufffd"]) == GenerationResult( | |
| "x", [3], "stop", 1, 1) | |
| def test_unicode_stop_spans_byte_tokens(): | |
| engine, native = make_engine(pieces={3: b"x\xe2", 4: b"\x82", 5: b"\xac", 6: b"!"}) | |
| result = engine.generate([1], max_new_tokens=4, stop=["\u20ac"]) | |
| assert result == GenerationResult("x", [3, 4, 5], "stop", 1, 3) | |
| assert native.sessions[0].generated_tokens == 3 | |
| def test_stop_does_not_match_temporary_utf8_replacement(stop): | |
| engine, _ = make_engine(pieces={3: b"x\xe2", 4: b"\x82", 5: b"\xac", 6: b"!"}) | |
| result = engine.generate([1], max_new_tokens=4, stop=[stop]) | |
| assert result == GenerationResult("x\u20ac!", [3, 4, 5, 6], "length", 1, 4) | |
| def test_eos_counted_but_not_rendered(eos): | |
| tokenizer = FakeTokenizer({3: b"a", 4: b"<eos>"}, special=[4, 5]) | |
| engine, native = make_engine(tokenizer=tokenizer, eos_token_id=eos) | |
| result = engine.generate([4, 1], max_new_tokens=4) | |
| assert result == GenerationResult("a", [3, 4], "stop", 2, 2) | |
| assert native.sessions[0].options["eos_token_ids"] == ([eos] if isinstance(eos, int) else eos) | |
| assert_released(engine, native) | |
| def test_eos_tokenizer_fallback_and_eos_at_budget(): | |
| tokenizer = FakeTokenizer(special=[3]) | |
| tokenizer.eos_token_id = 3 | |
| engine, _ = make_engine(tokenizer=tokenizer) | |
| assert engine.generate([1], max_new_tokens=1) == GenerationResult("", [3], "stop", 1, 1) | |
| def test_over_budget_is_clamped_and_long_prompt_rejected(): | |
| engine, native = make_engine(max_position_embeddings=2) | |
| assert list(engine.stream([1, 2], max_new_tokens=0)) == [GenerationChunk("", [], "length", 2, 0)] | |
| assert engine.generate([1, 2], max_new_tokens=0) == GenerationResult("", [], "length", 2, 0) | |
| assert engine.generate([1], max_new_tokens=1).completion_tokens == 1 | |
| # Over-budget requests clamp to the remaining window instead of failing. | |
| assert engine.generate([1], max_new_tokens=9) == GenerationResult("<3>", [3], "length", 1, 1) | |
| assert engine.generate([1, 2], max_new_tokens=5) == GenerationResult("", [], "length", 2, 0) | |
| with pytest.raises(ValueError, match="Prompt length exceeds"): | |
| engine.generate([1, 2, 3], max_new_tokens=0) | |
| assert len(native.sessions) == 5 | |
| assert_released(engine, native) | |
| def test_terminal_chunk_releases_engine_before_iterator_resumed(): | |
| engine, native = make_engine((3,)) | |
| stream = engine.stream([1], max_new_tokens=1) | |
| chunk = next(stream) | |
| assert chunk.finish_reason == "length" | |
| assert_released(engine, native) | |
| assert engine.generate([1], max_new_tokens=1).finish_reason == "length" | |
| stream.close() | |
| def test_precancelled_stream_and_generate(): | |
| engine, native = make_engine() | |
| cancel = threading.Event() | |
| cancel.set() | |
| assert list(engine.stream([1], cancel=cancel)) == [] | |
| with pytest.raises(RuntimeError, match="cancelled"): | |
| engine.generate([1], cancel=cancel) | |
| assert not native.sessions | |
| assert_released(engine, native) | |
| def test_suspended_generator_cleanup(action): | |
| engine, native = make_engine() | |
| cancel = threading.Event() | |
| before = set(threading.enumerate()) | |
| stream = engine.stream([1], max_new_tokens=4, cancel=cancel) | |
| assert next(stream).token_ids == [3] | |
| monitors = {thread for thread in threading.enumerate() if thread not in before and thread.name == "cism-cancel"} | |
| assert monitors | |
| assert engine._generation_lock.locked() | |
| if action == "cancel": | |
| cancel.set() | |
| assert native.sessions[0].cancelled.wait(2) | |
| assert list(stream) == [] | |
| elif action == "throw": | |
| with pytest.raises(LookupError, match="consumer failed"): | |
| stream.throw(LookupError("consumer failed")) | |
| else: | |
| stream.close() | |
| assert all(not thread.is_alive() for thread in monitors) | |
| assert_released(engine, native) | |
| assert engine.generate([1], max_new_tokens=1).token_ids == [3] | |
| def test_close_before_start_does_not_allocate(): | |
| engine, native = make_engine() | |
| stream = engine.stream([1]) | |
| stream.close() | |
| assert not native.sessions | |
| assert_released(engine, native) | |
| def test_cancel_interrupts_blocked_native_decode(monkeypatch, operation): | |
| engine, native = make_engine() | |
| entered = threading.Event() | |
| def blocked_decode(session, count, spec_k=0): | |
| entered.set() | |
| assert session.cancelled.wait(2), "Cancellation monitor failed to interrupt native work" | |
| return [] | |
| monkeypatch.setattr(FakeSession, "next_tokens", blocked_decode) | |
| cancel = threading.Event() | |
| def request(): | |
| if operation == "generate": | |
| return engine.generate([1], cancel=cancel) | |
| return list(engine.stream([1], cancel=cancel)) | |
| with ThreadPoolExecutor(max_workers=1) as executor: | |
| future = executor.submit(request) | |
| try: | |
| assert entered.wait(2) | |
| cancel.set() | |
| if operation == "generate": | |
| with pytest.raises(RuntimeError, match="cancelled"): | |
| future.result(timeout=3) | |
| else: | |
| assert future.result(timeout=3) == [] | |
| finally: | |
| cancel.set() | |
| assert_released(engine, native) | |
| def test_generate_cancellation_after_partial_output_raises(monkeypatch): | |
| engine, native = make_engine() | |
| cancel = threading.Event() | |
| original_decode = engine.tokenizer.decode | |
| def decode(tokens, **kwargs): | |
| cancel.set() | |
| return original_decode(tokens, **kwargs) | |
| monkeypatch.setattr(engine.tokenizer, "decode", decode) | |
| with pytest.raises(RuntimeError, match="cancelled"): | |
| engine.generate([1], max_new_tokens=4, cancel=cancel) | |
| assert native.sessions[0].generated_tokens == 1 | |
| assert_released(engine, native) | |
| def test_cancel_while_waiting_for_generation_lock(): | |
| engine, native = make_engine() | |
| first = engine.stream([1], max_new_tokens=4) | |
| next(first) | |
| cancel = threading.Event() | |
| started = threading.Event() | |
| def second_request(): | |
| started.set() | |
| return list(engine.stream([2], max_new_tokens=1, cancel=cancel)) | |
| try: | |
| with ThreadPoolExecutor(max_workers=1) as executor: | |
| future = executor.submit(second_request) | |
| try: | |
| assert started.wait(2) | |
| assert not future.done() | |
| cancel.set() | |
| assert future.result(timeout=2) == [] | |
| assert len(native.sessions) == 1 | |
| assert engine._generation_lock.locked() | |
| finally: | |
| cancel.set() | |
| first.close() | |
| finally: | |
| first.close() | |
| assert_released(engine, native) | |
| def test_generation_and_logits_share_lock(operation): | |
| engine, native = make_engine() | |
| first = engine.stream([1], max_new_tokens=4) | |
| next(first) | |
| entered = threading.Event() | |
| def second_request(): | |
| entered.set() | |
| if operation == "generate": | |
| return engine.generate([2], max_new_tokens=1) | |
| return engine.logits([2]) | |
| try: | |
| with ThreadPoolExecutor(max_workers=1) as executor: | |
| future = executor.submit(second_request) | |
| try: | |
| assert entered.wait(2) | |
| with pytest.raises(TimeoutError): | |
| future.result(timeout=0.1) | |
| assert len(native.sessions) == 1 | |
| assert not native.logits_calls | |
| finally: | |
| first.close() | |
| assert future.result(timeout=2) is not None | |
| finally: | |
| first.close() | |
| assert_released(engine, native) | |
| def test_failures_release_session_and_lock(monkeypatch, failure): | |
| engine, native = make_engine() | |
| def fail(*args, **kwargs): | |
| raise RuntimeError("injected failure") | |
| error, message = RuntimeError, "injected failure" | |
| if failure == "create": | |
| monkeypatch.setattr(native, "create_session", fail) | |
| elif failure == "decode": | |
| monkeypatch.setattr(FakeSession, "next_tokens", fail) | |
| elif failure == "tokenizer": | |
| monkeypatch.setattr(engine.tokenizer, "decode", fail) | |
| elif failure == "no_progress": | |
| monkeypatch.setattr(FakeSession, "next_tokens", lambda *args, **kwargs: []) | |
| message = "no progress" | |
| else: | |
| monkeypatch.setattr(engine.tokenizer, "decode", lambda tokens, **kwargs: "a" if len(tokens) == 1 else "b") | |
| error, message = ValueError, "rewrote emitted text" | |
| with pytest.raises(error, match=message): | |
| list(engine.stream([1], max_new_tokens=4)) | |
| assert_released(engine, native) | |
| def test_monitor_start_failure_releases_lock(monkeypatch): | |
| engine, native = make_engine() | |
| def fail_start(self): | |
| raise RuntimeError("cannot start monitor") | |
| monkeypatch.setattr(threading.Thread, "start", fail_start) | |
| with pytest.raises(RuntimeError): | |
| list(engine.stream([1], max_new_tokens=1)) | |
| assert_released(engine, native) | |
| def test_monitor_start_failure_preserves_original_error(monkeypatch): | |
| engine, _ = make_engine() | |
| def fail_start(self): | |
| raise RuntimeError("cannot start monitor") | |
| monkeypatch.setattr(threading.Thread, "start", fail_start) | |
| with pytest.raises(RuntimeError, match="^cannot start monitor$"): | |
| list(engine.stream([1], max_new_tokens=1)) | |
| def test_from_pretrained_forwards_import_options_and_resolved_revision(monkeypatch): | |
| from cism import loader | |
| calls = [] | |
| loaded = loader.LoadedModel({}, {}, FakeTokenizer(), "resolved-local-path", "commit-sha") | |
| def import_model(model, **options): | |
| calls.append((model, options)) | |
| return loaded | |
| def from_weights(cls, config, weights, tokenizer, **options): | |
| assert cls is Engine | |
| assert config is loaded.config | |
| assert weights is loaded.weights | |
| assert tokenizer is loaded.tokenizer | |
| assert options == {"model_id": "org/model", "precision": "int8", "revision": "commit-sha", | |
| "threads": 1, "act_precision": "fp32"} | |
| return "constructed-engine" | |
| monkeypatch.setattr(loader, "import_model", import_model) | |
| monkeypatch.setattr(Engine, "from_weights", classmethod(from_weights)) | |
| assert Engine.from_pretrained("org/model", precision="int8", revision="main", | |
| trust_remote_code=True, local_files_only=True, | |
| cache_dir="cache", threads=1) == "constructed-engine" | |
| assert calls == [("org/model", { | |
| "revision": "main", "trust_remote_code": True, | |
| "local_files_only": True, "cache_dir": "cache", | |
| })] | |
| def real_engine(request): | |
| # Lazy import keeps fake-only SDK tests independent of the native extension. | |
| from test_native import reference_logits, tiny_model | |
| config, weights = tiny_model(request.param) | |
| engine = Engine.from_weights(config, weights, FakeTokenizer(), | |
| model_id="tiny-" + request.param, revision="local-test") | |
| return engine, config, weights, reference_logits | |
| def test_from_weights_logits_and_greedy_oracle(real_engine): | |
| import numpy as np | |
| engine, config, weights, reference_logits = real_engine | |
| prompt = [2, 7, 1, 19, 4] | |
| np.testing.assert_allclose(engine.logits(prompt), reference_logits(config, weights, prompt), | |
| atol=8e-6, rtol=8e-5) | |
| expected = [] | |
| for _ in range(9): | |
| expected.append(int(np.argmax(reference_logits(config, weights, prompt + expected)))) | |
| result = engine.generate(prompt, max_new_tokens=9) | |
| assert result.token_ids == expected | |
| assert result.finish_reason == "length" | |
| assert result.prompt_tokens == len(prompt) | |
| assert result.completion_tokens == 9 | |
| assert result.text == "".join(f"<{token}>" for token in expected) | |
| assert engine.info["architecture"] == config["model_type"] | |
| assert engine.info["revision"] == "local-test" | |
| assert engine.info["precision"] == "fp32" | |
| def test_nll_windowing_and_validation(real_engine): | |
| import math | |
| import numpy as np | |
| engine, config, weights, reference_logits = real_engine | |
| tokens = [2, 7, 1, 19, 4, 11, 3, 5] | |
| scores = engine.nll(tokens) | |
| assert set(scores) == {"nll", "perplexity", "scored_tokens"} | |
| assert scores["scored_tokens"] == len(tokens) - 1 | |
| assert abs(scores["perplexity"] - math.exp(scores["nll"])) < 1e-9 * max(1.0, scores["perplexity"]) | |
| # Windowed scoring equals single-window scoring when everything fits. | |
| whole = engine.nll(tokens, window=64) | |
| assert whole["nll"] == scores["nll"] | |
| # Deterministic across repeated calls and identical via the text path. | |
| assert engine.nll(tokens)["nll"] == scores["nll"] | |
| # Split windows are scored with truncated context, so they can differ; | |
| # both must stay deterministic and aggregate over all scored tokens. | |
| split = engine.nll(tokens, window=4) | |
| assert split["scored_tokens"] == len(tokens) - 1 | |
| assert split["nll"] > 0 | |
| assert engine.nll(tokens[:2])["scored_tokens"] == 1 | |
| for bad in (0, 1, -3, "x"): | |
| with pytest.raises(ValueError, match="window"): | |
| engine.nll(tokens, window=bad) | |
| with pytest.raises(ValueError, match="at least 2 tokens"): | |
| engine.nll([5]) | |
| def test_from_weights_sampled_stream_consistency(real_engine, chunk_size): | |
| engine, _, _, _ = real_engine | |
| options = dict(max_new_tokens=13, temperature=0.9, top_p=0.8, top_k=11, seed=876) | |
| result = engine.generate([3, 1], **options, chunk_size=1) | |
| chunks = list(engine.stream([3, 1], **options, chunk_size=chunk_size)) | |
| assert [token for chunk in chunks for token in chunk.token_ids] == result.token_ids | |
| assert "".join(chunk.text for chunk in chunks) == result.text | |
| assert chunks[-1].completion_tokens == result.completion_tokens == 13 | |
| assert chunks[-1].finish_reason == result.finish_reason == "length" | |
| assert chunks[-1].prompt_tokens == result.prompt_tokens == 2 | |
| def test_from_weights_forwards_precision(real_engine, precision): | |
| import numpy as np | |
| from test_native import reconstructed_weights | |
| _, config, weights, reference_logits = real_engine | |
| engine = Engine.from_weights(config, weights, FakeTokenizer(), precision=precision) | |
| assert engine.info["precision"] == precision | |
| np.testing.assert_allclose(engine.logits([1, 2, 3]), | |
| reference_logits(config, reconstructed_weights(weights, precision), [1, 2, 3]), | |
| atol=8e-6, rtol=8e-5) | |
| def test_from_weights_eos_stop_and_zero_budget(real_engine): | |
| import numpy as np | |
| engine, config, weights, _ = real_engine | |
| eos = int(np.argmax(engine.logits([1, 2]))) | |
| stopped = Engine.from_weights({**config, "eos_token_id": [eos]}, weights, | |
| FakeTokenizer(special=[eos])) | |
| assert stopped.generate([1, 2], max_new_tokens=1) == GenerationResult("", [eos], "stop", 2, 1) | |
| assert engine.generate([1, 2], max_new_tokens=4, stop=[f"<{eos}>"]) == GenerationResult( | |
| "", [eos], "stop", 2, 1) | |
| assert engine.generate([1] * 80, max_new_tokens=0) == GenerationResult("", [], "length", 80, 0) | |
| assert engine.generate([1] * 80, max_new_tokens=1) == GenerationResult("", [], "length", 80, 0) | |
| with pytest.raises(ValueError): | |
| engine.logits([1] * 81) | |
| with pytest.raises(ValueError, match="top_k"): | |
| engine.generate([1], max_new_tokens=1, top_k=config["vocab_size"] + 1) | |
| assert not engine._generation_lock.locked() | |
| def test_from_weights_rejects_invalid_config(real_engine, change): | |
| _, config, weights, _ = real_engine | |
| with pytest.raises(ValueError): | |
| Engine.from_weights({**config, **change}, weights, FakeTokenizer()) | |
| def test_lock_and_unlock_pages_forwarding(): | |
| engine, native = make_engine() | |
| assert engine.lock_pages() == 123 | |
| assert native.pages_locked | |
| assert engine.unlock_pages() == 123 | |
| assert not native.pages_locked | |
| def test_scan_wrapper_is_serialized_and_forwarded(): | |
| engine, native = make_engine() | |
| assert engine.scan() == 123 | |
| assert native.scans == 1 | |
| assert not engine._generation_lock.locked() | |
| def test_keep_warm_rejects_invalid_interval(interval): | |
| engine, _ = make_engine() | |
| with pytest.raises(ValueError, match="interval"): | |
| engine.start_keep_warm(interval) | |
| assert not engine.keep_warm_active | |
| def test_keep_warm_sweeps_until_stopped(): | |
| engine, native = make_engine() | |
| engine.start_keep_warm(0.005) | |
| deadline = time.monotonic() + 2 | |
| while native.touches < 2 and time.monotonic() < deadline: | |
| time.sleep(0.005) | |
| assert native.touches >= 2 | |
| engine.stop_keep_warm() | |
| final = native.touches | |
| time.sleep(0.03) | |
| assert native.touches == final | |
| assert not engine.keep_warm_active | |
| assert engine.keep_warm_error is None | |
| def test_keep_warm_skips_generation_and_resumes(): | |
| engine, native = make_engine() | |
| engine._generation_lock.acquire() | |
| engine.start_keep_warm(0.005) | |
| try: | |
| time.sleep(0.06) | |
| assert native.touches == 0 | |
| finally: | |
| engine._generation_lock.release() | |
| try: | |
| deadline = time.monotonic() + 2 | |
| while native.touches == 0 and time.monotonic() < deadline: | |
| time.sleep(0.005) | |
| assert native.touches >= 1 | |
| finally: | |
| engine.stop_keep_warm() | |
| final = native.touches | |
| time.sleep(0.03) | |
| assert native.touches == final | |
| def test_keep_warm_double_start_and_touch_failure(): | |
| engine, native = make_engine() | |
| engine.start_keep_warm(0.05) | |
| try: | |
| with pytest.raises(RuntimeError, match="already running"): | |
| engine.start_keep_warm(0.05) | |
| finally: | |
| engine.stop_keep_warm() | |
| def broken(): | |
| raise RuntimeError("touch failed") | |
| monkeypatch = pytest.MonkeyPatch() | |
| monkeypatch.setattr(native, "touch", broken) | |
| try: | |
| engine.start_keep_warm(0.005) | |
| deadline = time.monotonic() + 2 | |
| while engine.keep_warm_active and time.monotonic() < deadline: | |
| time.sleep(0.005) | |
| assert isinstance(engine.keep_warm_error, RuntimeError) | |
| assert not engine.keep_warm_active | |
| finally: | |
| monkeypatch.undo() | |
| engine.stop_keep_warm() | |
| # A failed sweep leaves the engine usable. | |
| assert engine.generate([1], max_new_tokens=1).finish_reason == "length" | |
| def test_stop_keep_warm_without_start_is_noop(): | |
| engine, _ = make_engine() | |
| engine.stop_keep_warm() | |
| assert not engine.keep_warm_active | |