From 45fd01a15e7f6be1685a2ddc22e25f5da31044f1 Mon Sep 17 00:00:00 2001 From: Hermes Team Date: Mon, 24 Aug 2026 01:18:00 +0700 Subject: [PATCH] =?UTF-8?q?fix(agy):=20=D0=BF=D0=BE=D0=B4=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=83=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=BD=D1=8F=20=D1=83=D1=81=D0=B8=D0=BB=D0=B8=D1=8F=20=E2=80=94?= =?UTF-8?q?=20gemini-3.7-flash=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D0=BA=D0=B0=D0=BA=20=D0=B5=D1=81=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Владелец возразил на моё утверждение, что gemini-3.7-flash не существует. Он прав, утверждение было неверным, и я повторил его в четырёх заданиях. gemini-3.7-flash — настоящее семейство, уровень усилия у неё отдельный параметр. В интерфейсе Antigravity это видно прямо: пункт «Gemini 3.7 Flash» с вложенным выбором Low/Medium/High. В коде это отражено: _display_to_cli разбирает «Gemini 3.7 Flash (High)» в пару ("gemini-3.7-flash", "high"). Меня ввела в заблуждение первая колонка вывода agy models со склеенными идентификаторами. Настоящий дефект был в коде: _model_supported_efforts вызывала discover_models() БЕЗ профиля, то есть в глобальном окружении без входа. Карта поддерживаемых усилий оставалась пустой, подстановка уровня по умолчанию не срабатывала, и agy отвергал вызов с «requires --effort» — при совершенно настоящей модели. profile_id проведён через agy_generate в _model_supported_efforts. Проверено исполнением: gemini-3.7-flash без указания усилия отрабатывает и возвращает ответ. Моки в test_antigravity_concurrency приведены к терпимости по kwargs. Добавлена поправка agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md: ложное утверждение попало в A9, A11, A18 и B8, и без опровержения кто-то чинил бы несуществующую проблему или сломал рабочую конфигурацию. Тесты: 359 passed, ruff чисто. Co-Authored-By: Claude Opus 5 --- ...026-08-24-CORRECTION-gemini-model-names.md | 48 +++++++++++++++++++ src/antigravity_provider/agy_subprocess.py | 17 +++++-- .../router/adapters/antigravity_adapter.py | 4 +- tests/test_antigravity_concurrency.py | 4 +- 4 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md diff --git a/agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md b/agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md new file mode 100644 index 0000000..d1205cb --- /dev/null +++ b/agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md @@ -0,0 +1,48 @@ +# Поправка: `gemini-3.7-flash` — настоящая модель + +## Дата +2026-08-24 + +## Кому +Обоим исполнителям. Отменяет утверждение, повторённое в четырёх заданиях. + +--- + +## Что было сказано неверно + +В заданиях A9, A11, A18 и B8 ревьюер написал, что модели **`gemini-3.7-flash` у провайдера не существует** и что она «попала в конфигурацию через литерал в коде». Формулировки вроде: + +> у живого провайдера **`gemini-3.7-flash` не существует** + +**Это неверно.** Утверждение опровергнуто владельцем и проверено исполнением. + +## Как есть на самом деле + +`gemini-3.7-flash` — настоящее семейство моделей. Уровень усилия у неё **отдельный параметр**, а не часть имени. В интерфейсе Antigravity это видно прямо: пункт «Gemini 3.7 Flash» с вложенным выбором Low / Medium / High. + +В коде это уже отражено: `_display_to_cli` разбирает `Gemini 3.7 Flash (High)` в пару `("gemini-3.7-flash", "high")`. + +Меня ввёл в заблуждение вывод `agy models`: в первой колонке он печатает склеенные идентификаторы вида `gemini-3.7-flash-high`. Я принял их за настоящие имена моделей, а флаг `--model` ожидает базовое имя плюс `--effort`. + +## Настоящий дефект — и он исправлен + +Ошибка была не в конфигурации, а в коде. + +`_model_supported_efforts` вызывала `discover_models()` **без профиля** — то есть в глобальном окружении, где вход `agy` не выполнен. Карта поддерживаемых усилий оставалась пустой, подстановка уровня по умолчанию не срабатывала, и `agy` отвергал вызов: + +``` +invalid model selection (--model "gemini-3.7-flash" --effort ""): +--model gemini-3.7-flash requires --effort (available: low, medium, high) +``` + +Исправлено: `profile_id` проведён через `agy_generate` в `_model_supported_efforts`. Проверено исполнением — `gemini-3.7-flash` **без указания усилия** отрабатывает и возвращает ответ. + +## Что из этого следует + +1. **Конфигурацию владельца по этому поводу править не нужно.** `gemini-3.7-flash` у роли `orchestrator` и `gemini-3.6-flash-high` у роли `fast` — оба варианта допустимы. +2. **Валидация моделей не должна отвергать базовые имена без суффикса усилия.** Если сравниваете с обнаруженным списком, учитывайте, что там склеенные идентификаторы, а в конфигурации может стоять базовое имя. +3. Требование не подставлять модели литералом **остаётся в силе** — оно верное и связано с другим: списки вида `["grok-3","grok-2"]` и `["gemini-2.5-pro", …]` в коде действительно были выдуманы, и `gemini-2.5-*` у провайдера действительно нет. + +## Почему это записано отдельным документом + +Задания читаются как справочный материал, и ложное утверждение в четырёх из них означало бы, что кто-то починит несуществующую проблему или сломает рабочую конфигурацию. Ошибка ревьюера, а не исполнителей. diff --git a/src/antigravity_provider/agy_subprocess.py b/src/antigravity_provider/agy_subprocess.py index 1ed6171..7aa46bd 100644 --- a/src/antigravity_provider/agy_subprocess.py +++ b/src/antigravity_provider/agy_subprocess.py @@ -339,10 +339,16 @@ def check_profile_native_auth_status(profile_id: str) -> tuple[bool, str | None, return False, None, None -def _model_supported_efforts(agy_model: str) -> set[str]: - """Return the set of effort levels supported by *agy_model*.""" - # Ensure discovery has run - discover_models() +def _model_supported_efforts(agy_model: str, profile_id: str | None = None) -> set[str]: + """Return the set of effort levels supported by *agy_model*. + + Профиль обязателен: обнаружение читает учётные данные из HOME, и без + подмены окружения оно выполняется в глобальном, где вход не сделан. + Карта усилий тогда остаётся пустой, подстановка уровня по умолчанию не + срабатывает, и agy отвергает вызов с «requires --effort» — при том что + сама модель совершенно настоящая. + """ + discover_models(profile_id=profile_id) return _AGY_EFFORT_MAP.get(agy_model, set()) @@ -633,6 +639,7 @@ def _safe_env() -> dict[str, str]: def agy_generate( request: dict[str, Any], custom_env: dict[str, str] | None = None, + profile_id: str | None = None, ) -> dict[str, Any]: """Execute a chat completion via the ``agy`` subprocess.""" exe = get_agy_exe() @@ -660,7 +667,7 @@ def agy_generate( # Smart effort selection based on model capabilities. # Some models (gemini) REQUIRE --effort, others (claude, gpt) DON'T SUPPORT it. - supported = _model_supported_efforts(agy_model) + supported = _model_supported_efforts(agy_model, profile_id=profile_id) if supported: # Model supports specific efforts if not agy_effort: diff --git a/src/antigravity_provider/router/adapters/antigravity_adapter.py b/src/antigravity_provider/router/adapters/antigravity_adapter.py index 51e6989..0316f69 100644 --- a/src/antigravity_provider/router/adapters/antigravity_adapter.py +++ b/src/antigravity_provider/router/adapters/antigravity_adapter.py @@ -78,9 +78,9 @@ class AntigravityAdapter(BaseProviderAdapter): ) with _AGY_INVOCATION_LOCK: - res = agy_generate(req, custom_env=custom_env) + res = agy_generate(req, custom_env=custom_env, profile_id=profile.profile_id) else: - res = agy_generate(req, custom_env=custom_env) + res = agy_generate(req, custom_env=custom_env, profile_id=profile.profile_id) if isinstance(res, dict) and "error" in res: err_dict = res.get("error") diff --git a/tests/test_antigravity_concurrency.py b/tests/test_antigravity_concurrency.py index 960eb7d..78e9ba5 100644 --- a/tests/test_antigravity_concurrency.py +++ b/tests/test_antigravity_concurrency.py @@ -27,7 +27,7 @@ def test_concurrent_antigravity_credential_isolation(tmp_path, monkeypatch): def mock_load_profile_auth(prov, profile_id): return {"token": f"token_for_{profile_id}"} - def mock_agy_generate(req, custom_env=None): + def mock_agy_generate(req, custom_env=None, **kwargs): user_prof = custom_env.get("USERPROFILE") if custom_env else None observed_envs_during_run.append((req.get("profile_id"), user_prof)) time.sleep(0.05) @@ -65,7 +65,7 @@ def test_credential_restoration_on_subprocess_exception(tmp_path, monkeypatch): def mock_load_profile_auth(prov, profile_id): return {"token": "valid_token"} - def mock_agy_generate_fail(req, custom_env=None): + def mock_agy_generate_fail(req, custom_env=None, **kwargs): raise RuntimeError("CLI process crashed") with patch.object(ProfileAuthManager, "load_profile_auth", side_effect=mock_load_profile_auth), \