Refactor access card handling and update CLI output for tunnel operations
- Integrated `print_access_card` functionality into the `up` command for both tunneled and non-tunneled scenarios. - Removed the deprecated `print_mcp_snippet` function from the session management flow. - Updated the `tunnel_forwards` function to streamline port handling for LLM runtimes. - Enhanced test cases to reflect changes in access card printing and MCP snippet logging.
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
"""Pretty end-of-launch access card: all local URLs and API endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
|
||||
from rich.console import Console, Group
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
from rich.text import Text
|
||||
|
||||
from gpu_rent.config import Config
|
||||
from gpu_rent.llm_runtime import normalize_runtime
|
||||
from gpu_rent.state import load_state
|
||||
|
||||
Log = Callable[[str], None]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AccessLink:
|
||||
label: str
|
||||
url: str
|
||||
note: str = ""
|
||||
|
||||
|
||||
def resolve_llm_runtime(cfg: Config) -> str:
|
||||
runtime = normalize_runtime(cfg.llm_runtime)
|
||||
try:
|
||||
noted = (load_state().notes or {}).get("llm_runtime")
|
||||
if noted:
|
||||
runtime = normalize_runtime(str(noted))
|
||||
except Exception:
|
||||
pass
|
||||
return runtime
|
||||
|
||||
|
||||
def collect_access_links(cfg: Config, *, tunneled: bool) -> list[AccessLink]:
|
||||
"""Build the list of user-facing endpoints (unit-tested)."""
|
||||
port = cfg.swarmui_local_port
|
||||
base = f"http://127.0.0.1:{port}"
|
||||
links: list[AccessLink] = []
|
||||
if tunneled:
|
||||
links.extend(
|
||||
[
|
||||
AccessLink("SwarmUI UI", base, "браузер"),
|
||||
AccessLink("SwarmUI API", f"{base}/API/", "HTTP JSON"),
|
||||
AccessLink("SwarmUI MCP", f"{base}/mcp", "Cursor mcp.json"),
|
||||
]
|
||||
)
|
||||
runtime = resolve_llm_runtime(cfg)
|
||||
if runtime == "ollama":
|
||||
o = cfg.ollama_local_port
|
||||
links.extend(
|
||||
[
|
||||
AccessLink(
|
||||
"Ollama API",
|
||||
f"http://127.0.0.1:{o}",
|
||||
f"OLLAMA_HOST=http://127.0.0.1:{o}",
|
||||
),
|
||||
AccessLink("Ollama tags", f"http://127.0.0.1:{o}/api/tags", "список моделей"),
|
||||
AccessLink(
|
||||
"Ollama chat",
|
||||
f"http://127.0.0.1:{o}/api/chat",
|
||||
"POST generate",
|
||||
),
|
||||
]
|
||||
)
|
||||
elif runtime == "llamacpp":
|
||||
p = cfg.llamacpp_local_port
|
||||
links.extend(
|
||||
[
|
||||
AccessLink("llama.cpp", f"http://127.0.0.1:{p}", "OpenAI-compatible"),
|
||||
AccessLink(
|
||||
"OpenAI /v1",
|
||||
f"http://127.0.0.1:{p}/v1/chat/completions",
|
||||
"chat completions",
|
||||
),
|
||||
AccessLink("Models", f"http://127.0.0.1:{p}/v1/models", "list"),
|
||||
]
|
||||
)
|
||||
else:
|
||||
links.append(
|
||||
AccessLink(
|
||||
"Туннель",
|
||||
"gpu-rent tunnel --open",
|
||||
"локальные URL появятся после tunnel",
|
||||
)
|
||||
)
|
||||
return links
|
||||
|
||||
|
||||
def mcp_snippet_lines(cfg: Config) -> list[str]:
|
||||
port = cfg.swarmui_local_port
|
||||
return [
|
||||
"{",
|
||||
' "mcpServers": {',
|
||||
' "swarmui": {',
|
||||
f' "url": "http://127.0.0.1:{port}/mcp"',
|
||||
" }",
|
||||
" }",
|
||||
"}",
|
||||
]
|
||||
|
||||
|
||||
def render_access_panel(
|
||||
cfg: Config,
|
||||
*,
|
||||
tunneled: bool = True,
|
||||
host: str | None = None,
|
||||
title: str = "gpu-rent · доступы",
|
||||
) -> Panel:
|
||||
links = collect_access_links(cfg, tunneled=tunneled)
|
||||
table = Table(show_header=True, header_style="bold", box=None, pad_edge=False)
|
||||
table.add_column("сервис", style="cyan", no_wrap=True)
|
||||
table.add_column("URL / команда", style="bold white")
|
||||
table.add_column("заметка", style="dim")
|
||||
|
||||
for link in links:
|
||||
table.add_row(link.label, link.url, link.note)
|
||||
|
||||
cmds = Table(show_header=False, box=None, pad_edge=False)
|
||||
cmds.add_column(style="dim", no_wrap=True)
|
||||
cmds.add_column()
|
||||
cmds.add_row("открыть UI", "gpu-rent open")
|
||||
if resolve_llm_runtime(cfg) in {"ollama", "llamacpp"}:
|
||||
cmds.add_row("открыть LLM", "gpu-rent open --llm")
|
||||
cmds.add_row("hold killer", "gpu-rent hold")
|
||||
cmds.add_row("стоп GPU", "gpu-rent stop")
|
||||
if not tunneled:
|
||||
cmds.add_row("туннель", "gpu-rent tunnel --open")
|
||||
|
||||
mcp = Text("\n".join(mcp_snippet_lines(cfg)), style="green")
|
||||
subtitle = Text()
|
||||
if tunneled:
|
||||
subtitle.append("туннель слушает localhost", style="green")
|
||||
if host:
|
||||
subtitle.append(f" · VM {host}", style="dim")
|
||||
else:
|
||||
subtitle.append("облако готово, туннеля нет", style="yellow")
|
||||
if host:
|
||||
subtitle.append(f" · FIP {host}", style="dim")
|
||||
|
||||
body = Group(
|
||||
subtitle,
|
||||
Text(""),
|
||||
table,
|
||||
Text(""),
|
||||
Text("команды", style="bold"),
|
||||
cmds,
|
||||
Text(""),
|
||||
Text("Cursor MCP (вставь в mcp.json — файл сам не трогаем)", style="bold"),
|
||||
mcp,
|
||||
)
|
||||
return Panel(
|
||||
body,
|
||||
title=f"[bold]{title}[/bold]",
|
||||
border_style="bright_blue",
|
||||
padding=(1, 2),
|
||||
)
|
||||
|
||||
|
||||
def print_access_card(
|
||||
cfg: Config,
|
||||
*,
|
||||
tunneled: bool = True,
|
||||
host: str | None = None,
|
||||
console: Console | None = None,
|
||||
log: Log | None = None,
|
||||
) -> None:
|
||||
"""Print Rich panel; fall back to plain lines if needed."""
|
||||
panel = render_access_panel(cfg, tunneled=tunneled, host=host)
|
||||
if console is not None:
|
||||
console.print()
|
||||
console.print(panel)
|
||||
console.print()
|
||||
return
|
||||
if log is not None:
|
||||
# Plain fallback for non-Rich loggers
|
||||
log("")
|
||||
log("══ gpu-rent · доступы ══")
|
||||
for link in collect_access_links(cfg, tunneled=tunneled):
|
||||
extra = f" ({link.note})" if link.note else ""
|
||||
log(f" {link.label:14} {link.url}{extra}")
|
||||
log("— Cursor MCP —")
|
||||
for line in mcp_snippet_lines(cfg):
|
||||
log(line)
|
||||
log("hold: gpu-rent hold | stop: gpu-rent stop")
|
||||
log("")
|
||||
return
|
||||
Console(highlight=False, legacy_windows=False).print(panel)
|
||||
|
||||
|
||||
def print_mcp_snippet(cfg: Config, log: Log) -> None:
|
||||
"""Back-compat: plain MCP + links via log."""
|
||||
print_access_card(cfg, tunneled=True, log=log)
|
||||
Reference in New Issue
Block a user