diff --git a/src/antigravity_provider/agy_subprocess.py b/src/antigravity_provider/agy_subprocess.py index d2d42e8..1769767 100644 --- a/src/antigravity_provider/agy_subprocess.py +++ b/src/antigravity_provider/agy_subprocess.py @@ -756,6 +756,31 @@ def agy_generate( "--disable-slash-commands", "--print-timeout", f"{timeout}s", ] + # Каталог agy отдаёт идентификаторы с уровнем усилия: gemini-3.7-flash-high, + # -medium, -low. В профиле же хранится голое имя, и вызов уходил с пустым + # --effort: agy отвечал «gemini-3.7-flash requires --effort (available: low, + # medium, high)» и работа не начиналась. + if agy_model and not agy_effort: + base, _, tail = str(agy_model).rpartition("-") + if base and tail in ("low", "medium", "high"): + # Уровень зашит в самом имени — отделяем его. + agy_model, agy_effort = base, tail + else: + known = _AGY_EFFORT_MAP.get(agy_model) or set() + if known: + # Берём средний уровень, если он есть: он и по названию средний, + # и по расходу квоты. Иначе — любой доступный, по порядку. + for candidate in ("medium", "high", "low"): + if candidate in known: + agy_effort = candidate + break + if not agy_effort: + agy_effort = sorted(known)[0] + logger.info( + "agy_generate: у модели %s не задан уровень усилия, выбран %s из %s", + agy_model, agy_effort, sorted(known), + ) + if agy_model: cmd.extend(["--model", agy_model]) if agy_effort: diff --git a/src/antigravity_provider/router/connection_preflight.py b/src/antigravity_provider/router/connection_preflight.py index 951b716..d4ebb21 100644 --- a/src/antigravity_provider/router/connection_preflight.py +++ b/src/antigravity_provider/router/connection_preflight.py @@ -30,6 +30,20 @@ def validate_connection(provider, token="", base_url="", preferred_model=""): raise ValueError("Для этого провайдера используйте вход через авторизацию") if provider not in ("local", "vllm", "ollama") and not token: raise ValueError("Не указан API-ключ") + + # Подписочные аккаунты Codex (ChatGPT) и Claude работают по OAuth, а не + # по платформенному ключу. Проверка ниже обращается к каталогу моделей, + # где платформенному ключу нужны права api.model.read: подписочный + # токен там получает 403 или 401, хотя аккаунт исправен и в Hermes + # работает. Предупреждаем об этом заранее, чтобы отказ не выглядел + # поломкой аккаунта. + subscription_note = "" + if provider in ("openai-codex", "codex", "claude", "anthropic"): + subscription_note = ( + " Если это аккаунт по подписке (ChatGPT Plus/Pro или Claude Max), " + "ключ здесь не подойдёт: такие аккаунты подключаются входом по ссылке " + "на предыдущем шаге мастера, а не API-ключом." + ) headers = {"Content-Type": "application/json"} if provider == "claude": headers.update({"x-api-key": token, "anthropic-version": "2023-06-01"}) @@ -139,7 +153,9 @@ def validate_connection(provider, token="", base_url="", preferred_model=""): "data": {"models": []}, } if isinstance(exc, urllib.error.HTTPError): - reason = exc.reason or 'провайдер отклонил запрос' + reason = (exc.reason or 'провайдер отклонил запрос') + if exc.code in (401, 403) and subscription_note: + reason = str(reason) + subscription_note try: body = json.loads(exc.read(4096).decode('utf-8', errors='replace')) detail = body.get('error') or body.get('detail')