Refactor CLI and LLM runtime handling for improved user experience
- Removed deprecated console usage in favor of structured logging functions for error handling and user prompts. - Enhanced CLI prompts for LLM runtime and preset selection, utilizing menu helpers for better user interaction. - Updated GPU pool scanning output with improved formatting and error indication for clarity. - Refactored setup wizard to streamline LLM runtime and preset configuration, ensuring a more intuitive setup process. - Improved documentation and user feedback in CLI outputs to enhance overall usability.
This commit is contained in:
+99
-82
@@ -10,7 +10,6 @@ from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
from gpu_rent import __version__
|
||||
@@ -23,22 +22,15 @@ from gpu_rent.ssh_ops import interactive_ssh, run_ssh
|
||||
from gpu_rent.state import load_state, preempt_window_end
|
||||
from gpu_rent.provision import ensure_swarmui_running, seed_civitai, seed_extensions
|
||||
from gpu_rent.sync_files import pull_tree, push_tree
|
||||
from gpu_rent.term import console, err, log, ok, warn
|
||||
from gpu_rent.tunnel import run_tunnel
|
||||
|
||||
if sys.platform == "win32":
|
||||
for _stream in (sys.stdout, sys.stderr):
|
||||
try:
|
||||
_stream.reconfigure(encoding="utf-8", errors="replace")
|
||||
except (AttributeError, OSError):
|
||||
pass
|
||||
|
||||
app = typer.Typer(
|
||||
no_args_is_help=True,
|
||||
pretty_exceptions_enable=False,
|
||||
add_completion=False,
|
||||
help="Прерываемый GPU Selectel + SwarmUI на localhost:17801. Сначала: gpu-rent setup / doctor. Ключи: docs/setup.md",
|
||||
)
|
||||
console = Console(highlight=False, legacy_windows=False)
|
||||
|
||||
_DEBUG = False
|
||||
|
||||
@@ -54,7 +46,7 @@ def _root(
|
||||
def _die(exc: BaseException) -> None:
|
||||
if _DEBUG:
|
||||
traceback.print_exc()
|
||||
console.print(f"[red]{exc}[/red]")
|
||||
err(str(exc))
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@@ -368,7 +360,7 @@ def setup(
|
||||
install_watchdog=watchdog if watchdog is not None else (False if yes else None),
|
||||
confirm=None if yes and watchdog is None else confirm,
|
||||
ask=None if yes and llm is not None else ask,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
except Exception as exc:
|
||||
_die(exc)
|
||||
@@ -412,7 +404,6 @@ def up(
|
||||
from dataclasses import replace
|
||||
|
||||
from gpu_rent.llm_runtime import (
|
||||
PRESET_HELP,
|
||||
append_vars_llm_runtime,
|
||||
decide_runtime,
|
||||
ensure_ollama_manifest_from_example,
|
||||
@@ -438,11 +429,24 @@ def up(
|
||||
|
||||
asked_model_preset = False
|
||||
if not yes and runtime == "none" and not llm and not ollama and not llamacpp:
|
||||
choice = typer.prompt(
|
||||
"Поднять LLM рядом со SwarmUI? [none/ollama/llamacpp]",
|
||||
default="none",
|
||||
from gpu_rent.llm_runtime import (
|
||||
llamacpp_preset_menu,
|
||||
llm_runtime_menu,
|
||||
ollama_preset_menu,
|
||||
)
|
||||
from gpu_rent.prompts import prompt_menu
|
||||
|
||||
def _ask(msg: str, default: str = "") -> str:
|
||||
return typer.prompt(msg, default=default)
|
||||
|
||||
try:
|
||||
choice = prompt_menu(
|
||||
"LLM рядом со SwarmUI",
|
||||
llm_runtime_menu(),
|
||||
default="none",
|
||||
ask=_ask,
|
||||
show=log,
|
||||
)
|
||||
runtime = decide_runtime(
|
||||
flag=choice, ollama_flag=False, llamacpp_flag=False, from_config="none"
|
||||
)
|
||||
@@ -452,63 +456,82 @@ def up(
|
||||
append_vars_llm_runtime(vars_path(), runtime)
|
||||
if runtime == "ollama":
|
||||
ensure_ollama_manifest_from_example()
|
||||
console.print(PRESET_HELP)
|
||||
preset = typer.prompt("Ollama preset", default="recommended")
|
||||
if preset.strip().lower() not in {"keep", "example"}:
|
||||
write_ollama_models_preset(
|
||||
cfg.ollama_models_manifest, preset.strip().lower()
|
||||
try:
|
||||
preset = prompt_menu(
|
||||
"Ollama preset",
|
||||
ollama_preset_menu(include_keep=False),
|
||||
default="recommended",
|
||||
ask=_ask,
|
||||
show=log,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise GpuRentError(str(exc)) from exc
|
||||
if preset not in {"keep", "example"}:
|
||||
write_ollama_models_preset(cfg.ollama_models_manifest, preset)
|
||||
asked_model_preset = True
|
||||
elif runtime == "llamacpp":
|
||||
from gpu_rent.llm_runtime import (
|
||||
LLAMACPP_PRESET_HELP,
|
||||
ensure_llamacpp_manifest_from_example,
|
||||
write_llamacpp_models_preset,
|
||||
)
|
||||
|
||||
ensure_llamacpp_manifest_from_example()
|
||||
console.print(LLAMACPP_PRESET_HELP)
|
||||
preset = typer.prompt(
|
||||
"llama.cpp GGUF preset [recommended/light/stock/empty]",
|
||||
default="recommended",
|
||||
)
|
||||
if preset.strip().lower() not in {"keep", "example"}:
|
||||
write_llamacpp_models_preset(
|
||||
cfg.llamacpp_models_manifest, preset.strip().lower()
|
||||
try:
|
||||
preset = prompt_menu(
|
||||
"llama.cpp GGUF",
|
||||
llamacpp_preset_menu(include_keep=False),
|
||||
default="recommended",
|
||||
ask=_ask,
|
||||
show=log,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise GpuRentError(str(exc)) from exc
|
||||
if preset not in {"keep", "example"}:
|
||||
write_llamacpp_models_preset(cfg.llamacpp_models_manifest, preset)
|
||||
asked_model_preset = True
|
||||
|
||||
# Runtime уже в vars (напр. llamacpp) — всё равно спросить модель, default=keep.
|
||||
if not yes and not asked_model_preset and runtime == "llamacpp":
|
||||
# Runtime уже в vars — спросить пресет, default=keep.
|
||||
if not yes and not asked_model_preset and runtime in {"llamacpp", "ollama"}:
|
||||
from gpu_rent.llm_runtime import (
|
||||
LLAMACPP_PRESET_HELP,
|
||||
ensure_llamacpp_manifest_from_example,
|
||||
llamacpp_preset_menu,
|
||||
ollama_preset_menu,
|
||||
write_llamacpp_models_preset,
|
||||
)
|
||||
from gpu_rent.prompts import prompt_menu
|
||||
|
||||
ensure_llamacpp_manifest_from_example()
|
||||
console.print(LLAMACPP_PRESET_HELP)
|
||||
preset = typer.prompt(
|
||||
"llama.cpp GGUF preset [recommended/light/stock/empty/keep]",
|
||||
default="keep",
|
||||
)
|
||||
key = preset.strip().lower()
|
||||
if key not in {"keep", "example", ""}:
|
||||
write_llamacpp_models_preset(cfg.llamacpp_models_manifest, key)
|
||||
elif not yes and not asked_model_preset and runtime == "ollama":
|
||||
ensure_ollama_manifest_from_example()
|
||||
console.print(PRESET_HELP)
|
||||
preset = typer.prompt(
|
||||
"Ollama preset [recommended/light/stock/alt/empty/keep]",
|
||||
default="keep",
|
||||
)
|
||||
key = preset.strip().lower()
|
||||
if key not in {"keep", "example", ""}:
|
||||
write_ollama_models_preset(cfg.ollama_models_manifest, key)
|
||||
def _ask2(msg: str, default: str = "") -> str:
|
||||
return typer.prompt(msg, default=default)
|
||||
|
||||
try:
|
||||
if runtime == "llamacpp":
|
||||
ensure_llamacpp_manifest_from_example()
|
||||
key = prompt_menu(
|
||||
"llama.cpp GGUF",
|
||||
llamacpp_preset_menu(include_keep=True),
|
||||
default="keep",
|
||||
ask=_ask2,
|
||||
show=log,
|
||||
)
|
||||
if key not in {"keep", "example", ""}:
|
||||
write_llamacpp_models_preset(cfg.llamacpp_models_manifest, key)
|
||||
else:
|
||||
ensure_ollama_manifest_from_example()
|
||||
key = prompt_menu(
|
||||
"Ollama preset",
|
||||
ollama_preset_menu(include_keep=True),
|
||||
default="keep",
|
||||
ask=_ask2,
|
||||
show=log,
|
||||
)
|
||||
if key not in {"keep", "example", ""}:
|
||||
write_ollama_models_preset(cfg.ollama_models_manifest, key)
|
||||
except ValueError as exc:
|
||||
raise GpuRentError(str(exc)) from exc
|
||||
|
||||
cfg = replace(cfg, llm_runtime=runtime)
|
||||
if runtime != "none":
|
||||
console.print(f"LLM runtime: {runtime}")
|
||||
ok(f"LLM runtime: {runtime}")
|
||||
|
||||
def confirm(msg: str) -> bool:
|
||||
return typer.confirm(msg)
|
||||
@@ -525,7 +548,7 @@ def up(
|
||||
update=False if no_update else None,
|
||||
confirm=confirm,
|
||||
ask=None if yes else ask,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
if no_tunnel:
|
||||
from gpu_rent.access_card import print_access_card
|
||||
@@ -550,7 +573,7 @@ def up(
|
||||
cfg,
|
||||
state.floating_ip,
|
||||
open_browser=open_browser,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
@@ -563,7 +586,7 @@ def stop(
|
||||
"""Удалить compute и FIP, диски оставить."""
|
||||
try:
|
||||
cfg = load_config(require_auth=True)
|
||||
cmd_stop(cfg, no_pull=no_pull, log=lambda m: console.print(m))
|
||||
cmd_stop(cfg, no_pull=no_pull, log=log)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -579,7 +602,7 @@ def destroy(
|
||||
raise typer.Exit(1)
|
||||
try:
|
||||
cfg = load_config(require_auth=True)
|
||||
cmd_stop(cfg, destroy_disks=True, no_pull=no_pull, log=lambda m: console.print(m))
|
||||
cmd_stop(cfg, destroy_disks=True, no_pull=no_pull, log=log)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -636,7 +659,7 @@ def tunnel(
|
||||
cfg,
|
||||
state.floating_ip,
|
||||
open_browser=open_browser,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
@@ -654,9 +677,9 @@ def hold(
|
||||
|
||||
cfg, host = _live()
|
||||
if clear:
|
||||
clear_hold(cfg, host, log=lambda m: console.print(m))
|
||||
clear_hold(cfg, host, log=log)
|
||||
return
|
||||
set_hold(cfg, host, minutes=minutes, until=until, log=lambda m: console.print(m))
|
||||
set_hold(cfg, host, minutes=minutes, until=until, log=log)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -666,7 +689,7 @@ def seed_models() -> None:
|
||||
"""Докачать Civitai-манифест на живой диск."""
|
||||
try:
|
||||
cfg, host = _live()
|
||||
seed_civitai(cfg, host, log=lambda m: console.print(m))
|
||||
seed_civitai(cfg, host, log=log)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -677,9 +700,6 @@ def push_all() -> None:
|
||||
try:
|
||||
cfg, host = _live()
|
||||
|
||||
def log(msg: str) -> None:
|
||||
console.print(msg)
|
||||
|
||||
push_tree(cfg, host, cfg.local_models_dir, "/mnt/swarm_data/Models", log, models=True)
|
||||
push_tree(cfg, host, cfg.local_wildcards_dir, "/mnt/swarm_data/Data/Wildcards", log, models=False)
|
||||
push_tree(cfg, host, cfg.local_workflows_dir, "/mnt/swarm_data/CustomWorkflows", log, models=False)
|
||||
@@ -697,7 +717,7 @@ def push_models() -> None:
|
||||
host,
|
||||
cfg.local_models_dir,
|
||||
"/mnt/swarm_data/Models",
|
||||
lambda m: console.print(m),
|
||||
log,
|
||||
models=True,
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
@@ -714,7 +734,7 @@ def pull_output_cmd() -> None:
|
||||
host,
|
||||
"/mnt/swarm_data/Output",
|
||||
cfg.local_output_dir,
|
||||
lambda m: console.print(m),
|
||||
log,
|
||||
)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
@@ -726,9 +746,6 @@ def seed_extensions_cmd() -> None:
|
||||
try:
|
||||
cfg, host = _live()
|
||||
|
||||
def log(msg: str) -> None:
|
||||
console.print(msg)
|
||||
|
||||
seed_extensions(cfg, host, log)
|
||||
ensure_swarmui_running(cfg, host, log, restart=True)
|
||||
except GpuRentError as exc:
|
||||
@@ -768,9 +785,9 @@ def capture_root(
|
||||
host,
|
||||
dry_run=dry_run,
|
||||
kind_filter=kind,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
print_report(report, lambda m: console.print(m), dry_run=dry_run)
|
||||
print_report(report, log, dry_run=dry_run)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -796,11 +813,11 @@ def capture_models_cmd(
|
||||
None,
|
||||
dry_run=dry_run,
|
||||
kind_filter=kind,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
print_report(
|
||||
report,
|
||||
lambda m: console.print(m),
|
||||
log,
|
||||
dry_run=dry_run,
|
||||
show_models=True,
|
||||
show_extensions=False,
|
||||
@@ -823,11 +840,11 @@ def capture_extensions_cmd(
|
||||
host,
|
||||
None,
|
||||
dry_run=dry_run,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
print_report(
|
||||
report,
|
||||
lambda m: console.print(m),
|
||||
log,
|
||||
dry_run=dry_run,
|
||||
show_models=False,
|
||||
show_extensions=True,
|
||||
@@ -854,9 +871,9 @@ def capture_all_cmd(
|
||||
host,
|
||||
dry_run=dry_run,
|
||||
kind_filter=kind,
|
||||
log=lambda m: console.print(m),
|
||||
log=log,
|
||||
)
|
||||
print_report(report, lambda m: console.print(m), dry_run=dry_run)
|
||||
print_report(report, log, dry_run=dry_run)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -868,7 +885,7 @@ def resize_data(gb: int = typer.Option(..., "--gb", help="Новый разме
|
||||
from gpu_rent.resize import resize_data_volume
|
||||
|
||||
cfg = load_config(require_auth=True)
|
||||
resize_data_volume(cfg, gb, log=lambda m: console.print(m))
|
||||
resize_data_volume(cfg, gb, log=log)
|
||||
except GpuRentError as exc:
|
||||
_die(exc)
|
||||
|
||||
@@ -905,9 +922,9 @@ def watchdog_install(
|
||||
try:
|
||||
from gpu_rent.local_watchdog import install_watchdog
|
||||
|
||||
install_watchdog(interval_minutes=interval, log=lambda m: console.print(m))
|
||||
install_watchdog(interval_minutes=interval, log=log)
|
||||
except Exception as exc:
|
||||
console.print(f"[red]install fail:[/red] {exc}")
|
||||
err(f"install fail: {exc}")
|
||||
raise typer.Exit(1) from exc
|
||||
|
||||
|
||||
@@ -917,9 +934,9 @@ def watchdog_uninstall() -> None:
|
||||
try:
|
||||
from gpu_rent.local_watchdog import uninstall_watchdog
|
||||
|
||||
uninstall_watchdog(log=lambda m: console.print(m))
|
||||
uninstall_watchdog(log=log)
|
||||
except Exception as exc:
|
||||
console.print(f"[red]uninstall fail:[/red] {exc}")
|
||||
err(f"uninstall fail: {exc}")
|
||||
raise typer.Exit(1) from exc
|
||||
|
||||
|
||||
@@ -946,7 +963,7 @@ def watchdog_tick(
|
||||
try:
|
||||
from gpu_rent.local_watchdog import run_tick
|
||||
|
||||
decision = run_tick(dry_run=dry_run, log=lambda m: console.print(m))
|
||||
decision = run_tick(dry_run=dry_run, log=log)
|
||||
if decision.kind == "stop" and not dry_run:
|
||||
raise typer.Exit(0)
|
||||
except GpuRentError as exc:
|
||||
|
||||
Reference in New Issue
Block a user