hermes-hub/tests/test_router_error_path.py
Hermes Team e15f12a3bd fix(router): обработчик ошибок падал сам, уровень усилия не подставлялся
Найдено проверкой всех шести ролей на живой машине владельца. До правок
работали три роли из шести.

1. antigravity_adapter.classify_error возвращал ErrorCategory.UNKNOWN —
   значения с таким именем не существует, есть AUTH_REQUIRED, FATAL,
   INVALID_REQUEST, QUOTA_EXHAUSTED, RATE_LIMITED, TRANSIENT. Обращение
   роняло сам классификатор с AttributeError, то есть отказ происходил
   ровно там, где обрабатывался другой отказ, и маршрутизация обрывалась
   вместо перехода к резервному профилю. Заменено на FATAL по образцу
   codex и opencode: неразобранная ошибка не должна давать повторов.

2. Уровень усилия не подставлялся, если у конкретного профиля не выполнен
   вход agy: карта усилий строится обнаружением ЧЕРЕЗ этот профиль, и при
   неудаче оставалась пустой. Уровни же — свойство модели, а не аккаунта.
   Добавлен запасной источник: сохранённый на диске список моделей со
   склеенными идентификаторами вида gemini-3.7-flash-high, из которых
   уровни выводятся напрямую и переживают перезапуск.

После правок работают все шесть ролей, включая живое переключение
'fast': opengo-1 -> ag-w4.

Закрыто тестами, включая защиту от возврата несуществующей категории.

Тесты: 373 passed, ruff чисто.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 08:51:01 +07:00

60 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Путь обработки ошибок не должен падать сам.
Два дефекта, найденные проверкой всех шести ролей на живой машине:
1. antigravity_adapter.classify_error возвращал ErrorCategory.UNKNOWN —
значения с таким именем не существует. Обращение к нему роняло сам
классификатор, то есть отказ случался ровно там, где обрабатывался
другой отказ, и маршрутизация обрывалась вместо перехода к резерву.
2. Уровень усилия не подставлялся, если у конкретного профиля не выполнен
вход agy: карта усилий строится обнаружением через этот профиль. Уровни
же — свойство модели, а не аккаунта, и берутся из сохранённого списка.
"""
from __future__ import annotations
from antigravity_provider.router.adapters import get_adapter
from antigravity_provider.router.adapters.base_adapter import ErrorCategory
def test_error_category_has_no_phantom_members():
"""Классификаторы обязаны ссылаться только на существующие категории."""
valid = {m for m in dir(ErrorCategory) if not m.startswith("_")}
assert "UNKNOWN" not in valid, (
"если UNKNOWN добавили — обновите классификаторы, раньше его не было"
)
for name in ("AUTH_REQUIRED", "FATAL", "QUOTA_EXHAUSTED", "RATE_LIMITED", "TRANSIENT"):
assert name in valid
def test_unclassified_error_does_not_crash_classifier():
"""Неразобранная ошибка должна классифицироваться, а не ронять обработчик."""
for provider in ("antigravity", "openai-codex", "opencode-go", "grok", "claude"):
adapter = get_adapter(provider)
result = adapter.classify_error(Exception("нечто совершенно неразобранное"))
assert result.category, f"{provider}: классификатор вернул пустую категорию"
assert isinstance(result.category, str)
def test_effort_levels_derived_from_cached_model_ids(monkeypatch):
"""Уровни усилия берутся из склеенных идентификаторов, без обращения к профилю."""
import antigravity_provider.agy_subprocess as agy
from antigravity_provider.router import model_discovery
monkeypatch.setattr(agy, "discover_models", lambda profile_id=None: {})
monkeypatch.setattr(agy, "_AGY_EFFORT_MAP", {}, raising=False)
class _Svc:
@staticmethod
def get():
return _Svc()
def get_models(self, provider):
return ["gemini-3.7-flash-low", "gemini-3.7-flash-high", "claude-sonnet-4-6"]
monkeypatch.setattr(model_discovery, "ModelDiscoveryService", _Svc)
assert agy._model_supported_efforts("gemini-3.7-flash") == {"low", "high"}
# У модели без суффиксов уровней быть не должно — подставлять нечего.
assert agy._model_supported_efforts("claude-sonnet-4-6") == set()