- Added `ollama-models.yaml` to .gitignore and implemented logic to copy it in gpu-rent.ps1 and gpu-rent.sh. - Enhanced env.example to include new variables for LLM runtime options and local watchdog configuration. - Updated CLI commands to support LLM options during setup and execution, including new flags for Ollama and llama.cpp. - Improved documentation in cli.md and README.md to reflect changes in LLM integration and local watchdog functionality. - Adjusted architecture and decisions documentation to clarify the role of LLMs and local watchdog in the system.
81 lines
3.2 KiB
Python
81 lines
3.2 KiB
Python
"""User-facing prints: flavor list, cost warnings (no invented ₽)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from typing import Any
|
|
|
|
from gpu_rent.config import Config
|
|
from gpu_rent.inventory import FlavorInfo, looks_like_gpu, rank_flavors, resolve_flavor
|
|
|
|
Log = Callable[[str], None]
|
|
|
|
|
|
def list_ranked_flavors(flavors: list[Any], cfg: Config) -> list[FlavorInfo]:
|
|
gpu = [f for f in flavors if looks_like_gpu(f)]
|
|
return rank_flavors(gpu or flavors, cfg.flavor_preference)
|
|
|
|
|
|
def format_flavor_lines(ranked: list[FlavorInfo], picked: FlavorInfo | None = None) -> list[str]:
|
|
if not ranked:
|
|
return ["flavors: пусто (проверь регион / FLAVOR_PREFERENCE)"]
|
|
lines = ["flavors (по FLAVOR_PREFERENCE):"]
|
|
for i, info in enumerate(ranked, 1):
|
|
mark = " ← выберем" if picked and info.id == picked.id else ""
|
|
ram = f"{info.ram_mb // 1024}GB" if info.ram_mb else "?"
|
|
vcpu = str(info.vcpus) if info.vcpus is not None else "?"
|
|
label = info.label or "—"
|
|
lines.append(f" {i}. [{label}] {info.name} vCPU={vcpu} RAM={ram} id={info.id}{mark}")
|
|
return lines
|
|
|
|
|
|
def cost_and_risk_lines(cfg: Config, *, spot: bool, flavor_name: str) -> list[str]:
|
|
"""Honest billing notes — OpenStack has no ₽ prices."""
|
|
return [
|
|
f"план: {'preemptible ' if spot else ''}{flavor_name}, data {cfg.data_volume_size_gb} GB",
|
|
"₽: цены в OpenStack API нет — смотри панель Selectel (GPU ₽/час + диск ₽/мес).",
|
|
"диск data тарифицируется 24/7, даже когда GPU выключен (stop).",
|
|
f"idle-killer: {cfg.idle_grace_minutes} мин льготы после boot, потом "
|
|
f"{cfg.idle_minutes} мин пустой очереди → delete compute. Отложить: gpu-rent hold",
|
|
"preemptible: хостер может усыпить (~24 ч окно) → EXPIRED; tunnel сам unshelve, "
|
|
"или gpu-rent up",
|
|
"Ctrl+C на tunnel GPU не гасит — только gpu-rent stop или idle-killer. "
|
|
"Опционально: gpu-rent watchdog install — аварийное закрытие окна/ребут "
|
|
"после grace тоже stop (Ctrl+C по-прежнему detach)",
|
|
]
|
|
|
|
|
|
def print_up_preview(
|
|
cfg: Config,
|
|
flavors: list[Any],
|
|
*,
|
|
picked: FlavorInfo,
|
|
spot: bool,
|
|
log: Log,
|
|
) -> None:
|
|
ranked = list_ranked_flavors(flavors, cfg)
|
|
for line in format_flavor_lines(ranked, picked):
|
|
log(line)
|
|
log("")
|
|
for line in cost_and_risk_lines(cfg, spot=spot, flavor_name=picked.name):
|
|
log(f"! {line}")
|
|
|
|
|
|
def resolve_and_preview(
|
|
cfg: Config,
|
|
flavors: list[Any],
|
|
*,
|
|
explicit: str | None,
|
|
spot: bool,
|
|
log: Log,
|
|
) -> FlavorInfo:
|
|
picked = resolve_flavor(
|
|
flavors,
|
|
cfg.flavor_preference,
|
|
explicit=explicit,
|
|
default_id=cfg.default_flavor_id or None,
|
|
fallback=cfg.flavor_fallback,
|
|
)
|
|
print_up_preview(cfg, flavors, picked=picked, spot=spot, log=log)
|
|
return picked
|