Перейти к содержимому

Тесты

Окно терминала
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.