# Отчёт: Задание A5 — настоящая телеметрия вызовов и закрытие долгов Дата: 2026-08-21 ## Идентификаторы и границы - **START_HEAD (BASE_SHA)**: `f5d002c918c5e6383ee302636d1b28daae820ec4` - **Ветка**: `antigravity/telemetry` - **origin/main**: `f5d002c918c5e6383ee302636d1b28daae820ec4` - **Граница зоны Codex**: ни один файл в `src/antigravity_provider/router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py` **НЕ изменялся** (`git diff --name-only` по этим путям пуст). - **Тег `v0.1.1`**: **НЕ создавался** (в репозитории `hermes-hub`). --- ## 1. Сохранение телеметрии собственных вызовов (P0-1) Реализован сервис `TelemetryService` в `src/antigravity_provider/router/telemetry_service.py`: - По одной записи `TelemetryRecord` на каждую попытку вызова роутера: - `timestamp` (float epoch) и `iso_time` (UTC ISO-8601); - `role`, `profile_id`, `provider`, `model`; - `outcome` (`"success"`, `"failover"`, `"error"`, `"quota_exhausted"`, `"rate_limited"`, `"auth_required"`); - `latency_seconds` (замеренная длительность выполнения); - `prompt_tokens`, `completion_tokens`, `total_tokens` (извлекаются **строго из метаданных `usage`**, возвращенных провайдером; если провайдер не вернул `usage`, поля остаются `None` без эвристических догадок); - `cost_usd` (рассчитывается только при наличии пользовательского прайса); - `failover_count`, `error_category`; - `source`: `"own_measurement"`. - Ограниченный размер и надежность: - Кольцевой буфер в памяти (`maxlen=10000`); - Ротация файла лога `telemetry.jsonl` при достижении 5 МБ с сохранением до 3 архивов (`telemetry.jsonl.1`..); - Никаких секретов, токенов, авторизационных заголовков или содержимого запросов/ответов; - Сохранение истории между перезапусками (чтение последних записей с диска при инициализации). --- ## 2. Эмпирические агрегаты (P0-2) - В `TelemetryService.get_aggregates(...)` реализован расчет метрик за произвольное окно времени по провайдеру, профилю, модели и роли: - Латентность: P50 (медиана), P95, Max, общее число вызовов (`total_calls`); - Токены: `total_prompt_tokens`, `total_completion_tokens`, `total_tokens` (суммируются только сообщенные провайдером токены); - Переключения: `failovers_count`, `failover_reasons` (гистограмма причин переключения); - Доля ошибок: `error_rate = failed_calls / total_calls`; - Источник данных: `source = "own_measurement"`. - **Честное поведение при отсутствии вызовов**: если вызовов в окне не было, возвращаются `None` для латентностей, токенов и доли ошибок, а флаг `has_data=False` (никаких фальшивых нулей). --- ## 3. Стоимость только при наличии прайса (P1-3) - В `RouterConfig` и схему `router_profiles.yaml` добавлена опциональная секция `pricing`: ```yaml pricing: gemini-2.5-pro: input_cost_per_m: 1.25 output_cost_per_m: 5.00 gpt-4o: input_cost_per_m: 2.50 output_cost_per_m: 10.00 ``` - Если прайс для модели задан — рассчитывается реальная стоимость `cost_usd = (prompt_tokens * input_rate + completion_tokens * output_rate) / 1_000_000`. - Если прайс не задан — `cost_usd = None`. Цены в код не зашиваются. Пример прайса добавлен в `config/router_profiles.example.yaml`. --- ## 4. Обновление контракта (P1-4) - В `docs/UI_STATE_CONTRACT.md`: - Gap 12 переведен в **Closed (Self-Measured)**: собственная эмпирическая телеметрия роутера (`source: "own_measurement"`). - Описан раздел **8. Telemetry & Empirical Metrics Contract** со спецификацией полей и поведения при отсутствии данных. - В **Active Limitations (Gap 13)** оставлены истинно неизмеримые внешние показатели (RPS серверов провайдера, SLA uptime %, CPU/RAM/Disk/Network хоста), для которых UI обязан отображать `Н/Д` либо опускать карточки. --- ## 5. Закрытие трёх долгов (P1-5) 1. **`Registry.CurrentUser` в `HermesHubSetup.cs` (Закрыт)**: - В `HermesHubSetup.cs` регистрация в HKCU изолирована проверкой переменной окружения `HERMES_HUB_NO_REGISTRY == "1"`. - В боевом установщике запись в `HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall` необходима для корректного отображения в панели «Установка и удаление программ» Windows для пользователя без прав администратора. В тестовых прогонах реестр изолирован. 2. **`fastapi` / `uvicorn` в обязательных зависимостях (Закрыт)**: - `fastapi` и `uvicorn` полностью отсутствуют в секции `dependencies` файла `pyproject.toml` и вынесены в `[project.optional-dependencies] legacy`. 3. **Комментарии в `router_profiles.yaml` (Закрыт)**: - Функция `save_router_config` в `router_config.py` считывает существующие заголовочные комментарии и пустые строки (`existing_comments`) перед первым ключом и сохраняет их verbatim. Проверено юнит-тестом `test_yaml_comments_preservation_and_debts_closure`. --- ## 6. Результаты проверок - **Headless pytest** (Python 3.8): `pytest -v` → **185 passed, 22 skipped, 3 deselected in 9.76s** - **Full pytest** (Python 3.12 с `customtkinter`, `pillow`, `psutil`): `py -3.12 -m pytest -v` → **185 passed, 22 skipped, 3 deselected in 13.50s** - **Ruff linter**: `ruff check .` → **All checks passed!** - **Release Gate**: `python scripts/release_gate.py` → **7/7 PASSED** (`[RELEASE GATE: PASSED] All criteria verified. Ready for Candidate v0.1.1`) - **Live Update Feed**: `[MANIFEST_LIVE=True, PACKAGE_LIVE=True, PACKAGE_HASH_VERIFIED=True]` (sha256 `b5bbdea2a7a2157a26389266aab07ab3602bb00b4612065c48defec9d6fe909c`) - **UI Zone Isolation**: `0 files modified in UI area`