Files
EvoScientist/EvoScientist/llm/ollama_discovery.py
T
m4 421a664336 feat(runtime)!: complete legacy removal, local snapshot entries, and TTL cleanup
- Remove legacy provider profiles, admin-token auth, /model command,
  model picker widget, and config.yaml LLM fields (design doc section 10)
- Wire CLI/channels/cron and async sub-agents through the local snapshot
  entry; run creation rejects model config outside runtime_snapshot_id
- Add periodic run-snapshot TTL cleanup to the config service lifespan
- Isolate tests from the real config dir and activate the registry where
  run/model paths fail closed in bootstrap

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-21 18:10:23 +08:00

73 lines
2.7 KiB
Python

"""Ollama server probing — shared by onboard wizard and /model picker.
Ollama models are whatever the user has ``ollama pull``ed locally; they
cannot be enumerated in a static model catalog. Both the setup wizard and the
interactive model picker need to hit ``GET {base_url}/api/tags`` to see
what is actually installed.
This module keeps a single implementation of that probe. ``onboard.py``
uses the sync variant (it drives a synchronous questionary flow);
``/model`` uses the async variant (command dispatch is already ``async``
and benefits from ``httpx.AsyncClient``).
"""
from __future__ import annotations
def validate_ollama_connection(base_url: str) -> tuple[bool, str, list[str]]:
"""Sync probe. Returns ``(is_reachable, human_msg, model_names)``.
Used by the onboarding wizard. Verbatim semantics from the original
implementation that lived in ``config/onboard.py`` — 5 s timeout,
swallows all exceptions into a ``(False, error_msg, [])`` tuple.
"""
if not base_url:
return True, "Skipped (no URL provided)", []
try:
import httpx
resp = httpx.get(f"{base_url.rstrip('/')}/api/tags", timeout=5)
if resp.status_code == 200:
data = resp.json()
models = data.get("models", [])
names = [m.get("name", "?") for m in models]
if names:
preview = ", ".join(names[:5])
return True, f"Connected — {len(names)} model(s): {preview}", names
return True, "Connected (no models pulled yet)", []
return False, f"HTTP {resp.status_code}", []
except Exception as e:
return False, f"Cannot reach Ollama: {e}", []
async def discover_ollama_models(
base_url: str | None, *, timeout: float = 1.5
) -> list[str]:
"""Async probe for the ``/model`` picker.
Returns the list of installed model names, or an empty list on any
failure (unreachable daemon, timeout, HTTP error, malformed JSON).
Never raises — the picker must always open, even when the daemon
is down.
If ``base_url`` is falsy, returns ``[]`` immediately without making
an HTTP call. Implicit ``localhost:11434`` probing is deliberately
out of scope (see ``.issue_ollama_model_picker.md`` non-goals);
``ollama_base_url`` must be explicitly configured to activate.
"""
if not base_url:
return []
try:
import httpx
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get(f"{base_url.rstrip('/')}/api/tags")
if resp.status_code != 200:
return []
data = resp.json()
return [m.get("name", "") for m in data.get("models", []) if m.get("name")]
except Exception:
return []