fix(llm-tui): fix card layout overlap, handle HTTP 503 loading state, and auto-switch to status tab
This commit is contained in:
parent
ca74d9f321
commit
d3b0f9ba7a
1 changed files with 29 additions and 3 deletions
32
bin/llm-tui
32
bin/llm-tui
|
|
@ -220,6 +220,15 @@ def check_port_status(port: int) -> tuple[bool, str]:
|
||||||
data = json.loads(response.read().decode())
|
data = json.loads(response.read().decode())
|
||||||
status = data.get("status", "unknown")
|
status = data.get("status", "unknown")
|
||||||
return True, f"Online ({status})"
|
return True, f"Online ({status})"
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
if e.code == 503:
|
||||||
|
try:
|
||||||
|
data = json.loads(e.read().decode())
|
||||||
|
msg = data.get("error", {}).get("message", "Loading model")
|
||||||
|
return True, f"Loading ({msg})"
|
||||||
|
except Exception:
|
||||||
|
return True, "Loading model..."
|
||||||
|
return False, f"HTTP Error {e.code}"
|
||||||
except Exception:
|
except Exception:
|
||||||
return False, "Offline"
|
return False, "Offline"
|
||||||
|
|
||||||
|
|
@ -246,6 +255,7 @@ class LLMConfigApp(App):
|
||||||
border: solid #3b4252;
|
border: solid #3b4252;
|
||||||
padding: 1 2;
|
padding: 1 2;
|
||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
color: #88c0d0;
|
color: #88c0d0;
|
||||||
|
|
@ -302,18 +312,21 @@ class LLMConfigApp(App):
|
||||||
border-left: thick #88c0d0;
|
border-left: thick #88c0d0;
|
||||||
padding: 1 2;
|
padding: 1 2;
|
||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
#vram-meter {
|
#vram-meter {
|
||||||
background: #232834;
|
background: #232834;
|
||||||
border: solid #4c566a;
|
border: solid #4c566a;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
margin-top: 1;
|
margin-top: 1;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
#status-card {
|
#status-card {
|
||||||
background: #1e222b;
|
background: #1e222b;
|
||||||
border: solid #4c566a;
|
border: solid #4c566a;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
Log {
|
Log {
|
||||||
background: #0f1117;
|
background: #0f1117;
|
||||||
|
|
@ -339,6 +352,8 @@ class LLMConfigApp(App):
|
||||||
_sync_lock: bool = False
|
_sync_lock: bool = False
|
||||||
_last_selected_model: str = ""
|
_last_selected_model: str = ""
|
||||||
_last_selected_mmproj: str = ""
|
_last_selected_mmproj: str = ""
|
||||||
|
_last_log_size: int = -1
|
||||||
|
_last_log_name: str = ""
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Header(show_clock=True)
|
yield Header(show_clock=True)
|
||||||
|
|
@ -672,7 +687,12 @@ class LLMConfigApp(App):
|
||||||
lines = []
|
lines = []
|
||||||
for port, label in [(11434, "Planner / Solo"), (11435, "Worker 1"), (11436, "Worker 2")]:
|
for port, label in [(11434, "Planner / Solo"), (11435, "Worker 1"), (11436, "Worker 2")]:
|
||||||
online, msg = check_port_status(port)
|
online, msg = check_port_status(port)
|
||||||
badge = "[green]● ONLINE[/]" if online else "[yellow]○ OFFLINE[/]"
|
if not online:
|
||||||
|
badge = "[yellow]○ OFFLINE[/]"
|
||||||
|
elif "loading" in msg.lower():
|
||||||
|
badge = "[cyan]⏳ LOADING[/]"
|
||||||
|
else:
|
||||||
|
badge = "[green]● ONLINE[/]"
|
||||||
lines.append(f"Port {port} ({label}): {badge} - {msg}")
|
lines.append(f"Port {port} ({label}): {badge} - {msg}")
|
||||||
|
|
||||||
card = self.query_one("#status-card", Static)
|
card = self.query_one("#status-card", Static)
|
||||||
|
|
@ -684,8 +704,13 @@ class LLMConfigApp(App):
|
||||||
if logs:
|
if logs:
|
||||||
latest = logs[0]
|
latest = logs[0]
|
||||||
try:
|
try:
|
||||||
|
cur_size = latest.stat().st_size
|
||||||
|
if cur_size == self._last_log_size and latest.name == self._last_log_name:
|
||||||
|
return
|
||||||
|
self._last_log_size = cur_size
|
||||||
|
self._last_log_name = latest.name
|
||||||
with open(latest, "r", errors="ignore") as f:
|
with open(latest, "r", errors="ignore") as f:
|
||||||
f.seek(max(0, latest.stat().st_size - 8192))
|
f.seek(max(0, cur_size - 16384))
|
||||||
tail = f.read()
|
tail = f.read()
|
||||||
log_widget.clear()
|
log_widget.clear()
|
||||||
log_widget.write(f"--- Streaming {latest.name} ---\n" + tail)
|
log_widget.write(f"--- Streaming {latest.name} ---\n" + tail)
|
||||||
|
|
@ -732,7 +757,8 @@ class LLMConfigApp(App):
|
||||||
)
|
)
|
||||||
|
|
||||||
subprocess.Popen(cmd, env=env, cwd=str(SCRIPT_DIR), start_new_session=True)
|
subprocess.Popen(cmd, env=env, cwd=str(SCRIPT_DIR), start_new_session=True)
|
||||||
self.notify(f"Launching {preset} server. Check Status & Live Logs tab.", severity="information")
|
self.notify(f"Launching {preset} server. Switching to Status & Logs...", severity="information")
|
||||||
|
self.query_one(TabbedContent).active = "tab-status"
|
||||||
self.action_refresh_status()
|
self.action_refresh_status()
|
||||||
|
|
||||||
def action_stop(self) -> None:
|
def action_stop(self) -> None:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue