Pull requests / #1093
#1093 Monitor: Load/Unload split button + idle-unload slider, load busy guard, honest unload
open · @miskahm · 0 comentarios · En GitHub
Descripción
The Monitor tab gains one **Load/Unload split button** for the model: the main segment follows the model's state (Load when unloaded, Unload when loaded → `POST /load`, `/unload`), and the caret opens a dropdown with an **idle-unload slider** (`idle_unload_s`, `POST /config`). The endpoints and the key already existed; the page had no controls for them, and a change to `idle_unload_s` needed a restart.
**Load busy guard.** `POST /load` answers 409 `busy` while a request runs or waits, like `/unload` already did. The guard comes *before* the "already loaded" answer: with the engine up and generating, the old fast path returned 200 `loaded` and looked like it had acted while a request was running. `test_load_refused_while_busy_even_when_the_engine_is_already_up` covers it.
**Honest unload.** Unloading frees the VRAM (the engine process exits), but `self.info` was never cleared, so `/metrics` kept serving the last session's `expert_slots`, `expert_cache_mib`, `arena_mib`, `vram_free_mib` — the Monitor's "Experts in VRAM" read 13,440 / 18.0 GB with a 75% bar while the card was at 1.5%. `StrataEngine.LIVE_INFO` lists the keys that only mean something while the process runs; `unload()` pops them. The row now says `model unloaded · 36 MiB in use` (from `hw.gpu_mem_used`). `/v1/models` served the same fiction.
**The idle slider's scale.** A plain 0..120 minute scale puts "never" one pixel from "1 min" and gives the common 5..20 minute range a sixth of the track. The input is now a position (0..1000) and the minutes are a piecewise curve over it: 25 steps/min to 20 min, 10/min to 45 min, 3⅓/min above. Round-trip is exact for every whole minute and monotonic over all 1001 positions. The 2 h cap is enforced server-side (`("num", 7200)` in `runconfig.EDITABLE`), so About cannot round-trip past the slider.
**Slider-hold reset fix.** `renderIdle` runs on every 1 s poll and unconditionally reset the displayed value to the last saved `idle_set`, clobbering the live drag display — so sliding and holding snapped the number back to the saved value each tick. A `dragging` flag (set on `oninput`, cleared on `onchange` and `pointerup`) makes `renderIdle` return early mid-drag. Verified in headless Chromium: a poll tick mid-drag holds the display; with the guard off the same poll resets it.
**Chevron-only button.** The caret segment shows only the chevron (the TTL value is in the popup and the button's `title` tooltip), so it is narrow (27px vs the 73px Load/Unload segment).
**Tests:** `serve/test_server.py` (+155) and `serve/test_runconfig.py` (+51) cover the busy guard, the honest unload, the 2 h cap, and the page's fields. Full `serve` suite: 433 tests, 6 errors / 10 skipped — identical to the pure `v0.1.40` baseline (the 6 are environmental: 3 `HeapBpe`, 3 `regex`-module). The commit adds 13 tests, all passing.
**Live verification (7900 XTX, gfx1100/ROCm 10.0.0):** `Reply with exactly: OK` → `OK`/`stop`; with a request generating, `POST /load` and `POST /unload` return 409 `busy` and leave the engine loaded; the chevron-only button renders at 27px; a real 1 s poll mid-drag holds the slider display.
## Screenshots
The Model state card with the Load/Unload split button (model loaded → `Unload`; the chevron opens the idle popup):

The idle popup: the slider (`idle_unload_s`) at 30 min, ticks `never / 15 min / 45 min / 2 h`, no help paragraph:

The full Monitor view with the popup open:

En el sitio
Enlaces a install, modelos, releases.