# Второе занятие: пять проверок

Правим catalog.py и models.py. Готовый транспорт client.py менять не требуется. После правок перезапускаем `run_client.py`, сохраняя псевдоним и файл идентичности. Локальная ошибка должна быть исправлена до следующей проверки. Преподаватель может запускать проверку одного участника.

## 1. Присутствие
Запустите исходную раздатку, дождитесь запроса преподавателя или автоопроса каждые 5 минут. ФИО передаётся автоматически отдельно. Рабочий маршрут default/v1 уже есть, можно перенести свою творческую обработку из первой практики.

## 2. Разные модели
Напишите register_model и run_inference. Зарегистрируйте default, upper, reverse. Для первого шага допускается внутренний каталог по имени; для первой проверки version необязательна (нет/null или v1). default — ваша творческая мока, возвращающая строку. upper — точный text.upper(), reverse — точный text[::-1]. Никаких префиксов и worker_id внутри этих двух ответов. Отсутствующее model выбирает default. Для проверки default должен возвращать строку без исключения.

## 3. Разные версии
Расширьте ключ каталога до (model,version). Две функции frame/v1 и frame/v2 возвращают ровно три строки:
```
=====
исходный запрос без изменений
=====
```
```
-----
исходный запрос без изменений
-----
```
Без завершающего перевода строки. Проверочный запрос однострочный, включая пробелы и Unicode. У frame версия по умолчанию v1. Сервер вызывает v1, v2, снова v1, затем frame без версии. Метаданные ответа должны совпадать с исполнением. Реализация через обёртку/декоратор рекомендована, проверяется поведение. Повторная регистрация существующей пары должна вызывать ошибку, не заменять код.

## 4. Ответ-ошибка (дополнительно)
Неизвестное имя: MODEL_NOT_FOUND. Неизвестная версия существующей frame: VERSION_NOT_FOUND. Зарегистрируйте broken/v1 — функцию, которая выбрасывает исключение. Запускатор возвращает INFERENCE_ERROR. В готовом client.py поддержано преобразование `ValueError('КОД')` в поле error. Проверка после трёх ошибок вызывает upper: клиент должен остаться жив. Явно неправильный запрос нельзя подменять default.

### Точный контракт ошибок

| Проверяемый случай | Где проверять | Что сделать | Пример запроса | Условие зачёта |
|---|---|---|---|---|
| Нет такой модели | `catalog.py`, начало `run_inference` | `raise ValueError("MODEL_NOT_FOUND")` | `model="missing_abc", text="Привет"` | RESULT содержит error `MODEL_NOT_FOUND`, без output |
| Модель есть, версии нет | После проверки имени, до вызова функции | `raise ValueError("VERSION_NOT_FOUND")` | `model="frame", version="missing_abc"` | error `VERSION_NOT_FOUND`; не подменять запрос версией по умолчанию |
| Обработчик упал | `models.py`: зарегистрировать `broken/v1`, выбрасывающий исключение. В `catalog.py`: перехватить исключение выполнения | `raise ValueError("INFERENCE_ERROR")` | `model="broken", version="v1"` | error `INFERENCE_ERROR`; соединение сохраняется |
| Работа после ошибок | Обычный маршрут исполнения | Выполнить `upper` | `model="upper", text="Проба Ab"` | Точный ответ `ПРОБА AB`, а не ещё одна ошибка |

Готовый `client.py` сам превращает `ValueError("КОД")` в поле `error`. Не нужно создавать JSON и отправлять его внутри модели. Ошибки выбора модели/версии проверяйте до блока, который превращает сбой обработчика в INFERENCE_ERROR — иначе потеряется точный код.

Имена неизвестных моделей и версий сервер генерирует заново: нельзя проверять только буквальное `missing_abc`. Отсутствующая версия (`None`) — допустимый запрос версии по умолчанию, а не VERSION_NOT_FOUND.

Для бонусного установщика: заготовка выдаёт INSTALL_NOT_IMPLEMENTED; неудачная установка — INSTALL_ERROR. Для зачёта бонуса нужны INSTALLED и правильные ответы всех семи моделей. Ошибка установки не считается выполненным бонусом.

## 5. Пакет моделей с сервера (бонус)
Отдельная презентация: https://topic02.81.26.178.127.sslip.io/materials/bonus.html
Пошаговые средства Python и контракт: BONUS.md.

Реализуйте install_release(manifest). INSTALL передаёт `{"registry_url":"/registry/models"}`. Получите releases, пройдите по всему списку, скачайте и проверьте каждый файл, загрузите infer и зарегистрируйте соответствующие model/version. Сейчас это семь выпусков: server_echo, oracle_location, oracle_name, weather_mood, coin_oracle, dad_joke, ascii_cat (v1). Не зашивайте список в установщик.

После INSTALLED сервер вызовет каждый выпуск. Точное ожидаемое поведение видно в исходниках; все функции сохраняют входную строку. Отметка ставится только за весь пакет. Повторная установка одинакового выпуска не должна ломать клиент. Готового установщика в ученической раздатке нет; отдельные операции объяснены в бонусной презентации. Ошибки из задания 4 также разбираются там.

## Свой пульт (сквозное развитие)
Ваш отправитель из первой практики теперь выбирает model/version. Можно сохранять любимые запросы, сравнивать v1/v2, показывать каталог, историю и ошибки. Это личный инструмент для следующих занятий, отдельной обязательной проверки «домашки ради домашки» нет.

Проверяйте себя или коллегу кнопкой под нужным этапом в карточке общего дашборда. Для артефакта кнопка сначала запускает установку, затем проверку семи моделей у выбранного клиента.

## Что подтверждает серверная проверка
Сервер проверяет наблюдаемое поведение на запросах с новым случайным текстом: точный output, выбранную модель и версию, нужный error, выполнение следующего запроса после ошибки. Обычный ответ default не заменяет прохождение остальных заданий.

Чёрный ящик не доказывает структуру кода: каталог вместо if/elif, скачивание вместо заранее встроенной копии, запрет повторной регистрации, проверку контрольной суммы. Эти требования разбираются по коду и локальной самопроверке. 5/5 означает, что клиент прошёл пять проверок поведения, а не экспертизу каждой строки реализации.

Прежние успехи сохраняются. Красная причина последней неудачной попытки не отменяет уже полученную отметку. После успешной повторной проверки соответствующая причина исчезает.
