From 281ae15b120f68bfb0853df29306741957932784 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Fri, 21 Aug 2026 09:34:13 +0300 Subject: [PATCH] Enhance access link collection and backend status reporting - Updated the `collect_access_links` function to provide clearer user-facing endpoint labels and notes, particularly for non-tunneled scenarios. - Improved logging messages in the provisioning process to reflect the status of the SwarmUI and Ollama API, enhancing user feedback during setup. - Added human-readable status messages for backend loading and running states, improving clarity during the waiting process. - Updated tests to verify the new behavior and ensure accurate reporting of access links and backend statuses. --- src/gpu_rent/access_card.py | 95 ++++++++++++++++++--------------- src/gpu_rent/provision.py | 13 ++++- src/gpu_rent/ready.py | 10 +++- tests/test_access_card.py | 7 ++- tests/test_wait_backend_idle.py | 7 +++ 5 files changed, 85 insertions(+), 47 deletions(-) diff --git a/src/gpu_rent/access_card.py b/src/gpu_rent/access_card.py index 9c47e6d..550b31b 100644 --- a/src/gpu_rent/access_card.py +++ b/src/gpu_rent/access_card.py @@ -39,52 +39,60 @@ def resolve_llm_runtime(cfg: Config) -> str: def collect_access_links(cfg: Config, *, tunneled: bool) -> list[AccessLink]: - """Build the list of user-facing endpoints (unit-tested).""" + """Build the list of user-facing endpoints (unit-tested). + + When ``tunneled`` is False, still lists the same localhost URLs with a + «после tunnel» note — useful mid-``up`` before SSH forward is up. + """ links: list[AccessLink] = [] swarm = bool(getattr(cfg, "enable_swarmui", True)) - if tunneled: - if swarm: - port = cfg.swarmui_local_port - base = f"http://127.0.0.1:{port}" - 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", - ), - ] - ) - if not links: - links.append( + soon = "" if tunneled else "после tunnel" + if swarm: + port = cfg.swarmui_local_port + base = f"http://127.0.0.1:{port}" + links.extend( + [ + AccessLink("SwarmUI UI", base, soon or "браузер"), + AccessLink("SwarmUI API", f"{base}/API/", soon or "HTTP JSON"), + AccessLink("SwarmUI MCP", f"{base}/mcp", soon or "Cursor mcp.json"), + ] + ) + runtime = resolve_llm_runtime(cfg) + if runtime == "ollama": + o = cfg.ollama_local_port + links.extend( + [ AccessLink( - "Туннель", - "gpu-rent tunnel", - "нет сервисов — ENABLE_SWARMUI / LLM_RUNTIME", - ) - ) - else: + "Ollama API", + f"http://127.0.0.1:{o}", + soon or f"OLLAMA_HOST=http://127.0.0.1:{o}", + ), + AccessLink( + "Ollama tags", + f"http://127.0.0.1:{o}/api/tags", + soon or "список моделей", + ), + AccessLink( + "Ollama chat", + f"http://127.0.0.1:{o}/api/chat", + soon or "POST generate", + ), + ] + ) + if not links: links.append( AccessLink( "Туннель", - "gpu-rent tunnel --open", - "локальные URL появятся после tunnel", + "gpu-rent tunnel", + "нет сервисов — ENABLE_SWARMUI / LLM_RUNTIME", + ) + ) + elif not tunneled: + links.append( + AccessLink( + "Сейчас", + "ждём Idle → туннель", + "не открывай :7801 на ноутбуке — только :17801 после tunnel", ) ) return links @@ -139,7 +147,7 @@ def render_access_panel( if host: subtitle.append(f" · VM {host}", style="dim") else: - subtitle.append("облако готово, туннеля нет", style="yellow") + subtitle.append("URL ниже — после Idle откроется туннель", style="yellow") if host: subtitle.append(f" · FIP {host}", style="dim") @@ -191,11 +199,12 @@ def print_access_card( *, tunneled: bool = True, host: str | None = None, + title: str = "gpu-rent · доступы", 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) + panel = render_access_panel(cfg, tunneled=tunneled, host=host, title=title) if console is not None: console.print() console.print(panel) @@ -204,7 +213,7 @@ def print_access_card( if log is not None: # Plain fallback for non-Rich loggers log("") - log("══ gpu-rent · доступы ══") + log(f"══ {title} ══") try: notes = load_state().notes or {} if notes.get("idle_killer") == "failed": diff --git a/src/gpu_rent/provision.py b/src/gpu_rent/provision.py index 931c0f0..a374147 100644 --- a/src/gpu_rent/provision.py +++ b/src/gpu_rent/provision.py @@ -627,7 +627,16 @@ def provision_vm( _try_arm_idle_killer(cfg, host, log, conn=conn, server_id=server_id) if swarm: - log("SwarmUI слушает 127.0.0.1:7801 — gpu-rent tunnel") - if rt == "ollama": + from gpu_rent.access_card import print_access_card + from gpu_rent.term import console as rich_console + + print_access_card( + cfg, + tunneled=False, + host=host, + console=rich_console, + title="gpu-rent · URL после tunnel (ещё ждём Idle)", + ) + elif rt == "ollama": log(f"Ollama API → localhost:{cfg.ollama_local_port} (туннель)") log("Hold killer: gpu-rent hold | Стоп GPU: gpu-rent stop") diff --git a/src/gpu_rent/ready.py b/src/gpu_rent/ready.py index e694dae..7a7bc37 100644 --- a/src/gpu_rent/ready.py +++ b/src/gpu_rent/ready.py @@ -60,6 +60,11 @@ while time.time() < deadline: # No backends registered — Comfy never installed (Install wizard). # Not "Idle": treat as wait, never ready. print("BUSY backend=empty (нужен first-install Comfy)") + elif bstat == "loading": + print("BUSY backend=loading (Comfy стартует, ждём Idle)") + elif bstat == "running": + # Self-start often reports running while still warming / first load. + print("BUSY backend=running (Comfy прогрев, ждём Idle)") elif bstat in ("disabled", "all_disabled"): print(f"BUSY backend={bstat}") elif bstat == "idle": @@ -158,7 +163,10 @@ def wait_backend_idle( ) -> None: """Block until SwarmUI on the VM reports Idle backend (or timeout).""" deadline = time.time() + timeout - log("жду HTTP :7801 и Idle backend на VM…") + log( + "жду Idle backend на VM (loading/running — норма, Comfy прогревается; " + "ссылки :17801 — после ready + tunnel)" + ) last = "" while time.time() < deadline: try: diff --git a/tests/test_access_card.py b/tests/test_access_card.py index b25e8b7..3e83028 100644 --- a/tests/test_access_card.py +++ b/tests/test_access_card.py @@ -23,7 +23,12 @@ def test_collect_links_swarm_and_ollama(monkeypatch): def test_collect_links_no_tunnel(): links = collect_access_links(_Cfg(), tunneled=False) - assert links[0].url.startswith("gpu-rent tunnel") + labels = [x.label for x in links] + assert "SwarmUI UI" in labels + assert "Ollama API" in labels + assert any("17801" in x.url for x in links) + assert any(x.note == "после tunnel" for x in links) + assert any(x.label == "Сейчас" for x in links) def test_mcp_snippet_json(): diff --git a/tests/test_wait_backend_idle.py b/tests/test_wait_backend_idle.py index ba87d50..11ce5dc 100644 --- a/tests/test_wait_backend_idle.py +++ b/tests/test_wait_backend_idle.py @@ -15,6 +15,13 @@ def test_remote_poll_idle_is_ready(): assert "READY backend=" in _REMOTE_POLL +def test_remote_poll_loading_running_humanized(): + assert "Comfy стартует" in _REMOTE_POLL + assert "Comfy прогрев" in _REMOTE_POLL + assert 'bstat == "loading"' in _REMOTE_POLL + assert 'bstat == "running"' in _REMOTE_POLL + + def test_install_swarm_comfy_script_payload(): from importlib.resources import files