105 lines
8.3 KiB
Markdown
Executable file
105 lines
8.3 KiB
Markdown
Executable file
# Исправленные проблемы проекта YOLO Train WebUI
|
||
|
||
Дата исправления и повторной проверки: 2026-07-18
|
||
|
||
## Итог
|
||
|
||
Все 10 дефектов аудита от 2026-07-17 исправлены. WebUI снова проходит
|
||
синтаксическую проверку, серверный обработчик события начала обучения не зависает,
|
||
WebSocket-сообщения отправляются в event loop ASGI-сервера, а успешное завершение,
|
||
отмена и ошибка представлены отдельными состояниями.
|
||
|
||
Регрессионный набор расширен с 37 до 49 тестов.
|
||
|
||
| ID | Приоритет | Статус | Исправление |
|
||
|---|---|---|---|
|
||
| BUG-001 | Критический | Исправлено | Закрыт `try/catch`, удалено повторное объявление `configForm`, добавлен `node --check` в тесты |
|
||
| BUG-002 | Критический | Исправлено | Status broadcast вынесен за пределы `threading.Lock`; добавлен тест на отсутствие deadlock |
|
||
| SEC-001 | Критический при сетевой публикации | Исправлено | Compose публикует loopback, URL запрещены, пути ограничены доверенными корнями, restricted checkpoint loading включён |
|
||
| BUG-003 | Высокий | Исправлено | Все WebSocket send выполняются в ASGI loop через `run_coroutine_threadsafe`; ошибки логируются, сломанные сокеты удаляются |
|
||
| BUG-004 | Высокий | Исправлено | Введены состояния `succeeded`, `cancelled`, `failed`; ошибка после stop больше не маскируется как отмена |
|
||
| DOC-001 | Высокий | Исправлено | README полностью обновлён для WebUI, актуальных CLI-команд, Docker и модели безопасности |
|
||
| BUG-005 | Средний | Исправлено | Провалидированные классы всегда записываются в итоговый YAML и имеют приоритет над случайным корневым YAML |
|
||
| BUG-006 | Средний | Исправлено | Числа разбираются с проверкой `Number.isNaN`; нули сохраняются при чтении и восстановлении формы |
|
||
| BUG-007 | Средний | Исправлено | Серии графика добавляются динамически и выравниваются по эпохам, включая новые ключи метрик |
|
||
| BUILD-001 | Средний | Исправлено | Docker устанавливает frozen-набор из `uv.lock`; версия `uv` также зафиксирована |
|
||
|
||
## Жизненный цикл и WebSocket
|
||
|
||
- Событие `started` меняет состояние под lock, но отправляет статус только после
|
||
освобождения lock.
|
||
- Event loop запоминается при подключении WebSocket. Вызовы из фонового потока
|
||
передаются в него через `asyncio.run_coroutine_threadsafe()`.
|
||
- Отправки сериализуются `asyncio.Lock`, поэтому сообщения одного запуска сохраняют
|
||
порядок. Ошибка доставки попадает в журнал, а нерабочий клиент удаляется.
|
||
- Финальная классификация учитывает return code, stop-флаг, последнее
|
||
структурированное событие и факт принудительной остановки.
|
||
- Штатная кооперативная остановка даёт `cancelled`; ненулевой код после stop без
|
||
подтверждённой отмены даёт `failed`.
|
||
|
||
## Безопасность
|
||
|
||
- `docker-compose.yml` публикует `127.0.0.1:8000:8000`.
|
||
- Dataset, model и project не принимают URL.
|
||
- Локальные пути ограничены `datasets`, `models` и `runs`; дополнительные доверенные
|
||
корни задаются переменными `YOLO_WEBUI_DATA_ROOTS`,
|
||
`YOLO_WEBUI_MODEL_ROOTS`, `YOLO_WEBUI_RUN_ROOTS`.
|
||
- Проверка использует разрешённые абсолютные пути после `resolve()`, поэтому
|
||
symlink/`..` не позволяют выйти из доверенного корня.
|
||
- Имена профилей валидируются на сервере, а `last_run` нельзя перезаписать через
|
||
публичный endpoint профилей.
|
||
- `ULTRALYTICS_SAFE_LOAD=1` включён и в Python-процессе, и в Docker-образе.
|
||
- Для намеренной удалённой публикации по-прежнему нужен аутентифицирующий reverse
|
||
proxy; это явно указано в README.
|
||
|
||
## Frontend
|
||
|
||
- `app.js` снова является валидным JavaScript.
|
||
- `workers=0`, `patience=0` и `close_mosaic=0` проходят полный цикл
|
||
form → JSON → localStorage → form без замены default-значениями.
|
||
- График создаёт dataset при первом ключе метрики и добавляет новые серии в следующих
|
||
эпохах. Пропущенные значения дополняются `null`, поэтому точки не сдвигаются.
|
||
- UI и CSS отдельно отображают `succeeded`, `cancelled` и `failed`; старый
|
||
`finished` оставлен только как frontend-совместимость.
|
||
|
||
## Датасеты, Docker и документация
|
||
|
||
- Результат `read_classes()` безусловно становится `dataset_data["names"]`, сохраняя
|
||
при этом остальные ключи выбранного YAML (`kpt_shape`, `flip_idx` и другие).
|
||
- Docker копирует `pyproject.toml` вместе с `uv.lock` и выполняет
|
||
`uv sync --locked --no-dev`; обход lock-файла удалён.
|
||
- README описывает `uv run yolo-train-webui`, `uv run -m yolo_webui`, Compose,
|
||
структуру датасетов, MLflow и ограничения доверенных путей.
|
||
|
||
## Добавленные регрессионные проверки
|
||
|
||
Тесты теперь покрывают:
|
||
|
||
1. синтаксис browser JavaScript;
|
||
2. сохранение допустимых нулей и динамические серии Chart.js в Node smoke-test;
|
||
3. отсутствие deadlock на событии `started`;
|
||
4. доставку сообщения из background thread в loop WebSocket-сервера;
|
||
5. различие `succeeded` / `cancelled` / `failed`;
|
||
6. запрет URL и выходов за разрешённые корни;
|
||
7. защиту зарезервированного профиля `last_run`;
|
||
8. приоритет явно указанного `classes.txt` над корневым YAML.
|
||
|
||
## Выполненные проверки
|
||
|
||
```text
|
||
uv run pytest -q -> 49 passed, 1 warning
|
||
uv run python -m compileall -q src tests -> успешно
|
||
node --check src/yolo_webui/static/app.js -> успешно
|
||
node tests/frontend_smoke.js -> успешно
|
||
uv lock --check -> успешно
|
||
docker compose config -> успешно, host_ip=127.0.0.1
|
||
git diff --check -> успешно
|
||
```
|
||
|
||
Полная сборка Docker-образа локально не запускалась: Docker daemon недоступен.
|
||
Конфигурация Compose проверена отдельно, а соответствие lock-файла — через
|
||
`uv lock --check`.
|
||
|
||
Оставшееся предупреждение pytest относится к deprecated-связке
|
||
`fastapi.testclient`/`starlette.testclient` с `httpx`; оно не связано с исправленными
|
||
дефектами и не ломает тесты.
|