fix: agy не находился в Linux; Ollama Cloud нельзя было подключить по ключу
agy. Поиск проверял только раскладку Windows (%LOCALAPPDATA%/agy/bin) и PATH. В Linux утилита ставится в ~/.local/bin, а хаб запускается с урезанным окружением, где этого каталога в PATH нет. Владелец видел «agy executable not found» при установленной и работающей утилите — which agy находил её по адресу /home/ochenstarik/.local/bin/agy. Добавлены стандартные места Linux: ~/.local/bin, /usr/local/bin, /usr/bin, /snap/bin. Сообщение об отказе перечисляет проверенные пути. Отдельно различается «нет доступа к каталогу» и «файла нет». Ollama Cloud. Мастер требовал адрес локального сервера, которого у облачного аккаунта не существует: там только ключ. Теперь при заданном ключе недоступность локального адреса не считается отказом — аккаунт проверяется по каталогу ollama.com и принимается с честной оговоркой, что локальные модели недоступны. Проверено: 19 моделей каталога. Без ключа поведение прежнее. 609 passed, ruff clean, релизный гейт 10/10. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
266fc51adf
commit
f269891271
2 changed files with 62 additions and 6 deletions
|
|
@ -46,12 +46,32 @@ def _find_agy_exe() -> str:
|
|||
if env and Path(env).is_file():
|
||||
return env
|
||||
|
||||
# 2. Standard location based on hermes home parent
|
||||
# 2. Стандартные места установки.
|
||||
#
|
||||
# Раньше проверялась только раскладка Windows (%LOCALAPPDATA%/agy/bin), а в
|
||||
# Linux agy ставится в ~/.local/bin. Хаб запускается с урезанным окружением,
|
||||
# где этого каталога в PATH нет, и вход в Antigravity падал с «agy executable
|
||||
# not found» при установленной и работающей утилите.
|
||||
from antigravity_provider.paths import get_hermes_home
|
||||
|
||||
exe_name = "agy.exe" if os.name == "nt" else "agy"
|
||||
candidate = get_hermes_home().parent / "agy" / "bin" / exe_name
|
||||
if candidate.is_file():
|
||||
return str(candidate)
|
||||
candidates = [
|
||||
get_hermes_home().parent / "agy" / "bin" / exe_name,
|
||||
Path.home() / ".local" / "bin" / exe_name,
|
||||
Path("/usr/local/bin") / exe_name,
|
||||
Path("/usr/bin") / exe_name,
|
||||
Path("/snap/bin") / exe_name,
|
||||
]
|
||||
checked = []
|
||||
for candidate in candidates:
|
||||
checked.append(str(candidate))
|
||||
try:
|
||||
if candidate.is_file():
|
||||
return str(candidate)
|
||||
except OSError as exc:
|
||||
# Каталог может быть закрыт правами: это «не смогли проверить»,
|
||||
# а не «файла нет».
|
||||
checked[-1] = f"{candidate} (нет доступа: {exc.strerror or exc})"
|
||||
|
||||
# 3. PATH
|
||||
found = shutil.which("agy") or shutil.which("agy.exe")
|
||||
|
|
@ -59,8 +79,9 @@ def _find_agy_exe() -> str:
|
|||
return found
|
||||
|
||||
raise FileNotFoundError(
|
||||
"agy executable not found. Set the AGY_EXE_PATH environment "
|
||||
"variable, install agy, or ensure it is on PATH."
|
||||
"Утилита agy не найдена. Проверено: "
|
||||
+ "; ".join(checked)
|
||||
+ "; и PATH процесса. Задайте путь переменной AGY_EXE_PATH либо установите agy."
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,41 @@ def validate_connection(provider, token="", base_url="", preferred_model=""):
|
|||
except Exception as exc:
|
||||
exc_str = str(exc)
|
||||
exc_lower = exc_str.lower()
|
||||
# У Ollama Cloud локального сервера нет вовсе: там только ключ. Если
|
||||
# ключ задан, а локальный адрес не отвечает, это не отказ подключения —
|
||||
# аккаунт облачный. Проверяем его по общедоступному каталогу и
|
||||
# принимаем, честно сказав, что локальные модели недоступны.
|
||||
if provider == "ollama" and token and any(
|
||||
k in exc_lower for k in ("connection refused", "winerror 10061", "errno 111", "refused", "failed to connect", "target machine actively refused")
|
||||
):
|
||||
try:
|
||||
cloud = urllib.request.Request(
|
||||
"https://ollama.com/api/tags",
|
||||
headers={"Accept": "application/json", "Authorization": f"Bearer {token}"},
|
||||
)
|
||||
with urllib.request.urlopen(cloud, timeout=15) as response:
|
||||
payload = json.load(response)
|
||||
cloud_models = sorted({
|
||||
m.get("name") for m in (payload.get("models") or [])
|
||||
if isinstance(m, dict) and isinstance(m.get("name"), str)
|
||||
})
|
||||
return {
|
||||
"ok": True,
|
||||
"message": (
|
||||
f"Облачный аккаунт Ollama принят. Моделей в каталоге: {len(cloud_models)}. "
|
||||
f"Локальный сервер по адресу {base_url} не отвечает — локальные модели недоступны."
|
||||
),
|
||||
"data": {"models": cloud_models, "base_url": base_url, "cloud_only": True},
|
||||
}
|
||||
except Exception as cloud_exc:
|
||||
return {
|
||||
"ok": False,
|
||||
"message": (
|
||||
f"Локальный сервер {base_url} не отвечает, и облачный каталог Ollama недоступен: "
|
||||
f"{str(cloud_exc)[:160]}"
|
||||
),
|
||||
"data": {"models": []},
|
||||
}
|
||||
if provider == "ollama" and any(k in exc_lower for k in ("connection refused", "winerror 10061", "errno 111", "111", "refused", "failed to connect", "target machine actively refused")):
|
||||
return {
|
||||
"ok": False,
|
||||
|
|
|
|||
Loading…
Reference in a new issue