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:
@@ -0,0 +1,72 @@
|
||||
"""Numbered interactive menus (Enter = default index)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Sequence
|
||||
from dataclasses import dataclass
|
||||
|
||||
from rich.markup import escape
|
||||
|
||||
Ask = Callable[[str, str], str]
|
||||
Show = Callable[[str], None]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MenuItem:
|
||||
key: str
|
||||
label: str
|
||||
|
||||
|
||||
def resolve_menu_choice(
|
||||
raw: str,
|
||||
items: Sequence[MenuItem],
|
||||
*,
|
||||
default_key: str,
|
||||
) -> str:
|
||||
"""Accept 1-based index or exact key (case-insensitive). Empty → default."""
|
||||
text = (raw or "").strip().lower()
|
||||
if not text:
|
||||
return default_key
|
||||
if text.isdigit():
|
||||
idx = int(text)
|
||||
if 1 <= idx <= len(items):
|
||||
return items[idx - 1].key
|
||||
raise ValueError(f"выбор: жду 1…{len(items)}, получили {idx}")
|
||||
for item in items:
|
||||
if item.key.lower() == text:
|
||||
return item.key
|
||||
keys = ", ".join(f"{i}:{it.key}" for i, it in enumerate(items, 1))
|
||||
raise ValueError(f"выбор: жду номер или ключ ({keys}), получили {raw!r}")
|
||||
|
||||
|
||||
def prompt_menu(
|
||||
title: str,
|
||||
items: Sequence[MenuItem],
|
||||
*,
|
||||
default: str,
|
||||
ask: Ask,
|
||||
show: Show | None = None,
|
||||
) -> str:
|
||||
"""Print numbered list, ask for index (default = current recommendation)."""
|
||||
if not items:
|
||||
raise ValueError(f"{title}: пустое меню")
|
||||
default_idx = 1
|
||||
for i, item in enumerate(items, 1):
|
||||
if item.key == default:
|
||||
default_idx = i
|
||||
break
|
||||
safe_title = escape(title)
|
||||
lines = [f"[bold cyan]{safe_title}[/bold cyan]"]
|
||||
for i, item in enumerate(items, 1):
|
||||
label = escape(item.label)
|
||||
if i == default_idx:
|
||||
lines.append(f" [cyan]{i}.[/cyan] {label} [green]←[/green]")
|
||||
else:
|
||||
lines.append(f" [dim]{i}.[/dim] {label}")
|
||||
emit = show
|
||||
if emit is None:
|
||||
from gpu_rent.term import log as emit # type: ignore[assignment]
|
||||
for line in lines:
|
||||
emit(line)
|
||||
raw = ask(f"{title} [1-{len(items)}]", str(default_idx))
|
||||
return resolve_menu_choice(raw, items, default_key=items[default_idx - 1].key)
|
||||
Reference in New Issue
Block a user