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():
|
if env and Path(env).is_file():
|
||||||
return env
|
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
|
from antigravity_provider.paths import get_hermes_home
|
||||||
|
|
||||||
exe_name = "agy.exe" if os.name == "nt" else "agy"
|
exe_name = "agy.exe" if os.name == "nt" else "agy"
|
||||||
candidate = get_hermes_home().parent / "agy" / "bin" / exe_name
|
candidates = [
|
||||||
if candidate.is_file():
|
get_hermes_home().parent / "agy" / "bin" / exe_name,
|
||||||
return str(candidate)
|
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
|
# 3. PATH
|
||||||
found = shutil.which("agy") or shutil.which("agy.exe")
|
found = shutil.which("agy") or shutil.which("agy.exe")
|
||||||
|
|
@ -59,8 +79,9 @@ def _find_agy_exe() -> str:
|
||||||
return found
|
return found
|
||||||
|
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
"agy executable not found. Set the AGY_EXE_PATH environment "
|
"Утилита agy не найдена. Проверено: "
|
||||||
"variable, install agy, or ensure it is on PATH."
|
+ "; ".join(checked)
|
||||||
|
+ "; и PATH процесса. Задайте путь переменной AGY_EXE_PATH либо установите agy."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,41 @@ def validate_connection(provider, token="", base_url="", preferred_model=""):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
exc_str = str(exc)
|
exc_str = str(exc)
|
||||||
exc_lower = exc_str.lower()
|
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")):
|
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 {
|
return {
|
||||||
"ok": False,
|
"ok": False,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue