fix(ollama): облачный аккаунт объявлялся неработающим и безлимитным
Адрес. Адаптер всегда шёл на 127.0.0.1:11434, получал Connection refused и помечал аккаунт нерабочим — при том что каталог из девятнадцати облачных моделей у него получался. У Ollama Cloud локального сервера нет вовсе, есть только ключ. Теперь при заданном ключе и незаданном адресе адаптер идёт на ollama.com; явно указанный адрес по-прежнему в приоритете. Проверено: только ключ даёт https://ollama.com, заданный адрес — его же. Ключ. Как у NVIDIA и OpenRouter, он читался только из auth_config в router_profiles.yaml, куда мастер его не кладёт. Добавлено чтение из хранилища учётных данных. Лимиты. Провайдер ollama безусловно относился к локальным и получал подпись «Без ограничений (локальная модель)». Для облачного аккаунта это неправда: лимиты у него есть. Теперь облачный аккаунт (узнаётся по наличию ключа) показывает «Н/Д: лимиты не измерены — провайдер не сообщает их через API». Отдельно: отказ agy теперь доносит причину. Прежнее «код 1; каталог не получен» скрывало и текст ошибки, и главное — что agy запускается с HOME, подменённым на каталог профиля ради изоляции учётных данных. Если вход делался обычным agy в оболочке, ключи легли в настоящий домашний каталог, и профиль пуст. Теперь в сообщении и ответ agy, и использованный HOME. 610 passed, ruff clean, релизный гейт 10/10. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
afaf600f56
commit
9761362bc0
3 changed files with 75 additions and 3 deletions
|
|
@ -228,7 +228,18 @@ def discover_models(profile_id: str | None = None) -> dict[str, str]:
|
|||
raw = result.stdout.strip()
|
||||
if not raw or result.returncode != 0:
|
||||
if profile_id:
|
||||
raise RuntimeError(f"agy models: код {result.returncode}; каталог не получен")
|
||||
# Прежнее сообщение «код 1; каталог не получен» скрывало причину.
|
||||
# agy пишет её в stderr, и без неё непонятно главное: он
|
||||
# запускается с HOME, подменённым на каталог профиля (ради
|
||||
# изоляции учётных данных между аккаунтами). Если вход
|
||||
# выполнялся обычным agy в оболочке, ключи легли в настоящий
|
||||
# домашний каталог, и профиль пуст — отсюда отказ.
|
||||
detail = (result.stderr or "").strip() or (result.stdout or "").strip()
|
||||
detail = detail.splitlines()[-1][:300] if detail else "вывод пуст"
|
||||
raise RuntimeError(
|
||||
f"agy models: код {result.returncode}. Ответ agy: {detail}. "
|
||||
f"Запуск с HOME={env.get('HOME') or env.get('USERPROFILE') or 'не задан'}"
|
||||
)
|
||||
return dict(_AGY_MODEL_CACHE or {})
|
||||
except Exception:
|
||||
if profile_id:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from .local_adapter import LocalLLMAdapter
|
|||
logger = logging.getLogger("hermes.router.adapter.ollama")
|
||||
|
||||
DEFAULT_OLLAMA_BASE_URL = "http://127.0.0.1:11434"
|
||||
CLOUD_OLLAMA_BASE_URL = "https://ollama.com"
|
||||
DEFAULT_OLLAMA_MODELS = ["llama3:latest"]
|
||||
|
||||
|
||||
|
|
@ -25,11 +26,34 @@ class OllamaAdapter(LocalLLMAdapter):
|
|||
and native Ollama endpoints (/api/tags).
|
||||
"""
|
||||
|
||||
def _stored_base_url(self, profile: RouterProfileConfig) -> str:
|
||||
"""Адрес из хранилища учётных данных, если владелец его задавал."""
|
||||
try:
|
||||
from antigravity_provider.router.profile_manager import ProfileAuthManager
|
||||
|
||||
stored = ProfileAuthManager.load_profile_auth(profile.provider, profile.profile_id) or {}
|
||||
return str(stored.get("base_url") or "").strip()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _resolve_base_url(self, profile: RouterProfileConfig) -> str:
|
||||
"""Resolve base_url from profile custom_base_url, auth_config, or environment."""
|
||||
url = (
|
||||
"""Адрес сервера: локальный либо облачный.
|
||||
|
||||
У аккаунта Ollama Cloud локального сервера нет вовсе — только ключ.
|
||||
Раньше адаптер всегда шёл на 127.0.0.1:11434, получал «Connection
|
||||
refused» и объявлял облачный аккаунт неработающим, хотя каталог из
|
||||
девятнадцати моделей у него получался.
|
||||
"""
|
||||
explicit = (
|
||||
profile.custom_base_url
|
||||
or profile.auth_config.get("base_url")
|
||||
or self._stored_base_url(profile)
|
||||
)
|
||||
if not explicit and self._resolve_api_key(profile):
|
||||
return CLOUD_OLLAMA_BASE_URL
|
||||
|
||||
url = (
|
||||
explicit
|
||||
or os.environ.get("OLLAMA_BASE_URL")
|
||||
or os.environ.get("OLLAMA_HOST")
|
||||
or DEFAULT_OLLAMA_BASE_URL
|
||||
|
|
@ -45,6 +69,18 @@ class OllamaAdapter(LocalLLMAdapter):
|
|||
if key:
|
||||
return str(key).strip()
|
||||
|
||||
# Ключ, введённый в мастере, лежит в хранилище учётных данных, а не в
|
||||
# auth_config из router_profiles.yaml.
|
||||
try:
|
||||
from antigravity_provider.router.profile_manager import ProfileAuthManager
|
||||
|
||||
stored = ProfileAuthManager.load_profile_auth(profile.provider, profile.profile_id) or {}
|
||||
key = stored.get("api_key") or stored.get("token")
|
||||
if key:
|
||||
return str(key).strip()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
suffix = profile.profile_id.upper().replace("-", "_")
|
||||
for candidate in (f"OLLAMA_API_KEY_{suffix}", "OLLAMA_API_KEY", "OLLAMA_TOKEN"):
|
||||
val = os.environ.get(candidate, "").strip()
|
||||
|
|
|
|||
|
|
@ -918,6 +918,31 @@ class AccountQuotaService:
|
|||
def _generate_baseline_snapshot(self, provider: str, profile_id: str) -> QuotaSnapshot:
|
||||
"""Truthful offline baseline with provider-specific independent limit pools."""
|
||||
now = _utc_now()
|
||||
# У облачного аккаунта Ollama лимиты есть, и объявлять их
|
||||
# отсутствующими нельзя. Локальный сервер узнаётся по отсутствию
|
||||
# ключа: облачный доступ без ключа невозможен.
|
||||
if provider == "ollama":
|
||||
try:
|
||||
from .profile_manager import ProfileAuthManager
|
||||
|
||||
auth = ProfileAuthManager.load_profile_auth(provider, profile_id) or {}
|
||||
is_cloud = bool((auth.get("api_key") or auth.get("token") or "").strip())
|
||||
except Exception:
|
||||
is_cloud = False
|
||||
if is_cloud:
|
||||
return QuotaSnapshot(
|
||||
account_id=profile_id,
|
||||
provider=provider,
|
||||
buckets=[],
|
||||
fetched_at=now,
|
||||
source="cloud_provider",
|
||||
unavailable_reason=(
|
||||
"Н/Д: лимиты облачного аккаунта Ollama не измерены — "
|
||||
"провайдер не сообщает их через API"
|
||||
),
|
||||
is_loading=False,
|
||||
)
|
||||
|
||||
if provider in ("local", "local-llm", "llama.cpp", "ollama", "vllm"):
|
||||
b_unlimited = QuotaBucket(
|
||||
id="local.unlimited",
|
||||
|
|
|
|||
Loading…
Reference in a new issue