"""Unit tests: formats, estimator, compatibility engine, OpenAI helpers.""" from __future__ import annotations from pathlib import Path from llm_api.models import compat, formats from llm_api.models.estimator import estimate, kv_bytes_per_token, recommended_context from llm_api.worker.openai_types import StopMatcher, ThinkSplitter, parse_tool_calls from .conftest import make_gguf, make_mlx_model def test_parse_param_count(): assert formats.parse_param_count_from_name("Qwen3-30B-A3B-4bit") == (30_000_000_000, 3_000_000_000) assert formats.parse_param_count_from_name("gemma-3-4b-it-qat-4bit")[0] == 4_000_000_000 assert formats.parse_param_count_from_name("Qwen3-Embedding-0.6B-8bit")[0] == 600_000_000 assert formats.parse_param_count_from_name("all-MiniLM-L6-v2-4bit") == (None, None) assert formats.parse_param_count_from_name("embeddinggemma-300m-4bit")[0] == 300_000_000 def test_parse_quant(): assert formats.parse_quant_from_name("Qwen3-4B-Instruct-2507-4bit") == ("4bit", 4.0) assert formats.parse_quant_from_name("gpt-oss-20b-MXFP4-Q8")[0] == "MXFP4" q, b = formats.parse_quant_from_name("gemma-3-1b-it-Q4_K_M") assert q == "Q4_K_M" and b == 4.5 assert formats.parse_quant_from_name("Qwen3-Embedding-0.6B-4bit-DWQ") == ("DWQ-4bit", 4.0) assert formats.parse_quant_from_name("Qwen3.8-27B-bf16") == ("bf16", 16) def test_family(): assert formats.guess_family("Qwen3.6-35B-A3B-4bit") == "qwen" assert formats.guess_family("gemma-4-26b-a4b-it-4bit") == "gemma" assert formats.guess_family("Devstral-Small-2-24B") == "mistral" assert formats.guess_family("gpt-oss-20b-MXFP4-Q8") == "gpt-oss" assert formats.guess_family("Something-Unknown", "phi3") == "phi" def test_size_class(): assert formats.size_class(3, 45) == "TINY" assert formats.size_class(7, 45) == "SMALL" assert formats.size_class(15, 45) == "MEDIUM" assert formats.size_class(30, 45) == "LARGE" assert formats.size_class(40, 45) == "XL" assert formats.size_class(50, 45) == "TOO_LARGE" def test_kv_and_estimate(): kv = kv_bytes_per_token(36, 8, 128, 16) assert kv == 2 * 36 * 8 * 128 * 2 est = estimate(2_400_000_000, "mlx", kv, 32768) assert 9 < est.total_gb < 11 ctx, e2 = recommended_context(2_400_000_000, "mlx", kv, 262144, 45) assert ctx == 32768 # capped at the preferred context ctx3, _ = recommended_context(40 * 1024**3, "mlx", kv * 4, 131072, 45) assert ctx3 is not None and ctx3 <= 8192 def test_compat_statuses(monkeypatch): monkeypatch.setattr(compat, "mlx_available", lambda: True) monkeypatch.setattr(compat, "mlx_lm_model_types", lambda: {"qwen3"}) kv = kv_bytes_per_token(36, 8, 128) ok = compat.evaluate(runtime="mlx", weights_bytes=2_400_000_000, kv_per_token=kv, max_context=131072, model_type="qwen3", architecture="Qwen3ForCausalLM", vision=False, embedding=False, reranker=False, budget_gb=45, absolute_gb=50, llamacpp_available=True) assert ok.status == compat.COMPATIBLE and ok.recommended_context == 32768 too_big = compat.evaluate(runtime="mlx", weights_bytes=60 * 1024**3, kv_per_token=kv, max_context=131072, model_type="qwen3", architecture=None, vision=False, embedding=False, reranker=False, budget_gb=45, absolute_gb=50, llamacpp_available=True) assert too_big.status == compat.INCOMPATIBLE and not too_big.compatible swap = compat.evaluate(runtime="mlx", weights_bytes=44 * 1024**3, kv_per_token=kv, max_context=131072, model_type="qwen3", architecture=None, vision=False, embedding=False, reranker=False, budget_gb=45, absolute_gb=50, llamacpp_available=True) assert swap.status == compat.NOT_RECOMMENDED unknown = compat.evaluate(runtime="mlx", weights_bytes=1e9, kv_per_token=kv, max_context=8192, model_type="mystery", architecture=None, vision=False, embedding=False, reranker=False, budget_gb=45, absolute_gb=50, llamacpp_available=True) assert unknown.status == compat.INCOMPATIBLE no_llama = compat.evaluate(runtime="llamacpp", weights_bytes=1e9, kv_per_token=kv, max_context=8192, model_type="llama", architecture="llama", vision=False, embedding=False, reranker=False, budget_gb=45, absolute_gb=50, llamacpp_available=False, weights_file="x.gguf") assert no_llama.status == compat.INCOMPATIBLE exp = compat.evaluate(runtime="llamacpp", weights_bytes=1e9, kv_per_token=kv, max_context=8192, model_type="weird", architecture="weird", vision=False, embedding=False, reranker=False, budget_gb=45, absolute_gb=50, llamacpp_available=True, weights_file="x.gguf") assert exp.status == compat.EXPERIMENTAL def test_gguf_reader(tmp_path: Path): p = tmp_path / "m-Q4_K_M.gguf" make_gguf(p, arch="llama", layers=8, kv_heads=4, head_dim=64, ctx=4096) info = formats.read_gguf(p) assert info.architecture == "llama" and info.n_layers == 8 and info.n_kv_heads == 4 and info.head_dim == 64 assert info.context_length == 4096 and info.file_type_label == "Q4_K_M" assert info.param_count == 1_000_000 def test_safetensors_params(tmp_path: Path): d = tmp_path / "m" make_mlx_model(d, layers=2, hidden=256, bits=4) params, nbytes = formats.count_safetensors_params([d / "model.safetensors"], 4) # 2 layers * (1024 x 256) + embeddings 1000*256 assert params == 2 * 1024 * 256 + 1000 * 256 assert nbytes > 0 def test_hf_config_parse(): cfg = {"model_type": "qwen3", "architectures": ["Qwen3ForCausalLM"], "num_hidden_layers": 36, "num_attention_heads": 32, "num_key_value_heads": 8, "hidden_size": 2560, "head_dim": 128, "max_position_embeddings": 262144, "quantization": {"bits": 4, "group_size": 64}} p = formats.parse_hf_config(cfg) assert p["n_layers"] == 36 and p["n_kv_heads"] == 8 and p["head_dim"] == 128 and p["quant_bits"] == 4 and not p["vision"] v = formats.parse_hf_config({"model_type": "qwen3_vl", "text_config": {"num_hidden_layers": 2}, "vision_config": {}}) assert v["vision"] and v["n_layers"] == 2 def test_stop_matcher(): sm = StopMatcher(["", "User:"]) out = sm.feed("Hello <") assert out == "Hello " and not sm.done out += sm.feed("/s> tail") assert out == "Hello " and sm.done sm2 = StopMatcher(["STOP"]) assert sm2.feed("abc ST") == "abc " assert sm2.feed("art") == "STart" assert sm2.flush() == "" def test_think_splitter(): ts = ThinkSplitter("", "") r, c = ts.feed("reasoning here\n\nanswer") assert r2 == "" and c2 == "answer" def test_tool_calls(): rest, calls = parse_tool_calls('Sure.\n\n{"name": "get_weather", "arguments": {"city": "Montreal"}}\n') assert rest == "Sure." and len(calls) == 1 and calls[0]["function"]["name"] == "get_weather" assert '"city": "Montreal"' in calls[0]["function"]["arguments"]