Тесты
uv run pytest # из корня: llm/tests и hf-proxy/tests (testpaths в pyproject.toml)cd llm && uv run pytest # только библиотекаuv run pytest llm/tests/models/test_kv_cache.py -k mistral # один файл, фильтр по имениuv run pytest --cov # с покрытием (нужен uv sync --extra test)Около 1030 тестов llm и 130 тестов hf-proxy (с учётом параметризации) проходят меньше чем за минуту на CPU. Сеть не нужна: тесты сверки с HuggingFace строят случайные модели transformers локально. Без установленного transformers они пропускаются (pytest.importorskip).
Два теста в test_attention_mask.py пропускаются всегда — это задокументированное ограничение: у Mistral и Mixtral со скользящим окном нули в середине маски меняют состав окна, и сравнивать не с чем.
Как устроены
Заголовок раздела «Как устроены»llm/tests/├── core/ # каждый блок core/ отдельно: формы, формулы, пограничные случаи├── models/ # модели: контракт, кэш, маски, генерация, сохранение, инициализация, сверка с HF├── tokenizers/ # BaseTokenizer, BPETokenizer (включая сверку с HuggingFace tokenizers)├── datasets/ # содержимое примеров: токены, паддинг, маски, метки├── training/ # Trainer, оптимизатор, расписание└── test_source.py # проверки исходников (сырые строки для LaTeX в докстрингах)hf-proxy/tests/ # адаптеры модели, конфига, токенизатора, утилитыТесты, общие для всех моделей, параметризованы по списку моделей: test_model_contract.py, test_attention_mask.py, test_kv_cache.py, test_save_load.py, test_state_dict.py, test_generate_args.py. Новая модель добавляется в их списки.
Что проверять
Заголовок раздела «Что проверять»Тест должен проверять результат, а не только форму тензора. Приёмы, на которых держится проект:
- Сверка с эталоном. Модель — со случайной моделью
transformersна тех же весах (test_*_hf_parity.py:torch.allclose(logits, expected, atol=1e-4)и совпадение greedy-генерации); блок — с наивной реализацией формулы (MoE — с циклом по токенам,test_matches_naive_per_token_reference); токенизатор — с BPE изtokenizers. - Инварианты. Префилл кусками через кэш совпадает с полным
forward; генерация с кэшем — без кэша; каждая строка батча с паддингом — с этой строкой без паддинга;save→loadдаёт те же логиты. - Эталонные числа. Небольшие примеры, посчитанные вручную (как в упражнениях пособия): loss на заданных логитах, разбиение слова по слияниям.
- Регрессия. Тест на исправленную ошибку должен падать на старом коде. Проверяйте это: временно откатите исправление в
src/(например, отдельным WIP-коммитом) и запустите новый тест.
Детерминизм: фиксируйте torch.manual_seed в начале теста и держите dropout: 0.0 в конфиге, если сравниваете выходы.
Тесты сверки с HF и pad_token_id. transformers при generate(..., pad_token_id=0) без явной маски строит attention_mask сам и принимает токен 0 в промпте за паддинг. Передавайте attention_mask=torch.ones_like(prompt), иначе результат зависит от того, выпал ли 0 в случайном промпте.
Сайт документации
Заголовок раздела «Сайт документации»Сборку сайта проверяет CI в PR, где меняются docs/ или site/. Локально:
cd site && npm ci && npm run build # статический сайт в site/distПосле переименования глав или якорей проверьте, что ссылки не сломались: в собранном сайте каждая ссылка на страницу …/#якорь должна вести на существующий id.