Update LLM support for llama.cpp and enhance configuration management

- Added support for `llamacpp-models.yaml` in `.gitignore` and implemented logic to copy it in `gpu-rent.ps1` and `gpu-rent.sh`.
- Enhanced CLI to prompt for llama.cpp model presets during setup and execution, improving user experience.
- Updated configuration handling to include `llamacpp_models_manifest` and related functions for managing llama.cpp models.
- Improved documentation in `cli.md` and `llm.md` to reflect changes in llama.cpp integration and model management.
- Refactored provisioning logic to handle llama.cpp model downloads and configurations effectively.
This commit is contained in:
Leonid Pershin
2026-08-21 06:25:12 +03:00
parent 2ccb03f7d2
commit 64f93b4bf6
18 changed files with 607 additions and 32 deletions
+90
View File
@@ -3,12 +3,15 @@
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
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]
Ask = Callable[[str, str], str]
Confirm = Callable[[str], bool]
def list_ranked_flavors(flavors: list[Any], cfg: Config) -> list[FlavorInfo]:
@@ -61,6 +64,93 @@ def print_up_preview(
log(f"! {line}")
@dataclass(frozen=True)
class ServerPlan:
flavor: FlavorInfo
data_gb: int
spot: bool
def prompt_server_plan(
cfg: Config,
flavors: list[Any],
*,
picked: FlavorInfo,
spot: bool,
ask: Ask,
confirm: Confirm | None = None,
) -> ServerPlan:
"""Interactive Selectel VM knobs before create (flavor / disk / spot)."""
ranked = list_ranked_flavors(flavors, cfg)
if not ranked:
return ServerPlan(flavor=picked, data_gb=cfg.data_volume_size_gb, spot=spot)
default_idx = 1
for i, info in enumerate(ranked, 1):
if info.id == picked.id:
default_idx = i
break
raw_idx = ask(
f"Flavor Selectel [1-{len(ranked)}] (Enter = рекомендация)",
str(default_idx),
).strip()
try:
idx = int(raw_idx)
except ValueError as exc:
raise ValueError(f"номер flavor: жду 1…{len(ranked)}, получили {raw_idx!r}") from exc
if idx < 1 or idx > len(ranked):
raise ValueError(f"номер flavor: жду 1…{len(ranked)}, получили {idx}")
chosen = ranked[idx - 1]
raw_gb = ask(
"Data disk GB (тариф 24/7; рост только вверх)",
str(cfg.data_volume_size_gb),
).strip()
try:
data_gb = int(raw_gb)
except ValueError as exc:
raise ValueError(f"Data disk GB: жду число, получили {raw_gb!r}") from exc
if data_gb < 20:
raise ValueError("Data disk GB: минимум 20")
spot_default = "Y" if spot else "n"
raw_spot = (
ask(
"Preemptible GPU (дешевле, могут усыпить ~24ч)? [Y/n]",
spot_default,
)
.strip()
.lower()
)
if raw_spot in {"", "y", "yes", "1", "true", "on"}:
use_spot = True
elif raw_spot in {"n", "no", "0", "false", "off"}:
use_spot = False
else:
raise ValueError(f"preemptible: жду Y/n, получили {raw_spot!r}")
changed = (
chosen.id != picked.id
or data_gb != cfg.data_volume_size_gb
or use_spot != spot
)
if confirm and changed and confirm("Запомнить flavor/disk/spot в gpu-rent.vars?"):
from gpu_rent.paths import vars_path
from gpu_rent.varsfile import upsert_vars
upsert_vars(
vars_path(),
{
"DEFAULT_FLAVOR_ID": chosen.id,
"DATA_VOLUME_SIZE_GB": str(data_gb),
"DEFAULT_SPOT": "true" if use_spot else "false",
},
)
return ServerPlan(flavor=chosen, data_gb=data_gb, spot=use_spot)
def resolve_and_preview(
cfg: Config,
flavors: list[Any],