Files
gpu-rent/src/gpu_rent/prompts.py
T
Leonid Pershin e7784473a2 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.
2026-08-21 06:31:15 +03:00

73 lines
2.2 KiB
Python

"""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)