b1f9e87d6a
mcp-infra-server를 gpu-services/infra/로 통합. core/ 순수 로직은 Agent/NanoClaude에서도 직접 import 가능. 도구: docker_status, docker_logs, service_health, disk_usage, tailscale_status, ollama_models, mlx_models. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
98 lines
2.8 KiB
Python
98 lines
2.8 KiB
Python
"""Model inventory tools — Ollama and MLX model listing."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
|
|
from ..config import validate_host, HOSTS
|
|
from ..schemas import ModelsResult, ModelInfo
|
|
from .ssh import run_command, SSHError
|
|
|
|
|
|
def _now() -> str:
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
def _parse_ollama_list(output: str) -> list[ModelInfo]:
|
|
"""Parse `ollama list` output."""
|
|
models = []
|
|
for line in output.strip().splitlines()[1:]: # skip header
|
|
parts = line.split()
|
|
if len(parts) < 2:
|
|
continue
|
|
model_id = parts[0]
|
|
# Remaining fields vary: ID, SIZE, MODIFIED
|
|
size = parts[2] + " " + parts[3] if len(parts) > 3 else ""
|
|
modified = " ".join(parts[4:]) if len(parts) > 4 else ""
|
|
models.append(ModelInfo(id=model_id, size=size, modified=modified))
|
|
return models
|
|
|
|
|
|
async def ollama_models(host: str) -> ModelsResult:
|
|
"""List Ollama models on a host."""
|
|
try:
|
|
cfg = validate_host("ollama_models", host)
|
|
except ValueError as e:
|
|
return ModelsResult(
|
|
ok=False, checked_at=_now(), host=host, source="ollama",
|
|
error_type="parse_error", error=str(e),
|
|
)
|
|
|
|
try:
|
|
stdout, _ = await run_command(cfg, "ollama list")
|
|
except SSHError as e:
|
|
return ModelsResult(
|
|
ok=False, checked_at=_now(), host=host, source="ollama",
|
|
error_type=e.error_type, error=str(e),
|
|
)
|
|
|
|
models = _parse_ollama_list(stdout)
|
|
return ModelsResult(
|
|
ok=True,
|
|
checked_at=_now(),
|
|
host=host,
|
|
source="ollama",
|
|
models=models,
|
|
raw=stdout.strip(),
|
|
)
|
|
|
|
|
|
async def mlx_models() -> ModelsResult:
|
|
"""List MLX models loaded on Mac mini."""
|
|
cfg = HOSTS["macmini"]
|
|
try:
|
|
stdout, _ = await run_command(cfg, "curl -sf http://localhost:8800/v1/models")
|
|
except SSHError as e:
|
|
return ModelsResult(
|
|
ok=False, checked_at=_now(), host="macmini", source="mlx",
|
|
error_type=e.error_type, error=str(e),
|
|
)
|
|
|
|
try:
|
|
data = json.loads(stdout)
|
|
model_list = data.get("data", [])
|
|
models = [
|
|
ModelInfo(
|
|
id=m.get("id", "unknown"),
|
|
size=str(m.get("size", "")),
|
|
modified=str(m.get("created", "")),
|
|
)
|
|
for m in model_list
|
|
]
|
|
except (json.JSONDecodeError, KeyError) as e:
|
|
return ModelsResult(
|
|
ok=False, checked_at=_now(), host="macmini", source="mlx",
|
|
error_type="parse_error", error=f"JSON 파싱 실패: {e}",
|
|
raw=stdout.strip(),
|
|
)
|
|
|
|
return ModelsResult(
|
|
ok=True,
|
|
checked_at=_now(),
|
|
host="macmini",
|
|
source="mlx",
|
|
models=models,
|
|
raw=stdout.strip(),
|
|
)
|