diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-05 12:53:03 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-05 12:53:03 +0200 |
| commit | f0c92d6b9938bd6bb2d6e24aeb833fed175a9222 (patch) | |
| tree | b82c1cd1c079fdebe1c548991c30074ada303664 | |
| parent | c4e3406c8241ada9b9d7069149f88ab55c3b5952 (diff) | |
| download | quickshell-f0c92d6b9938bd6bb2d6e24aeb833fed175a9222.tar.gz quickshell-f0c92d6b9938bd6bb2d6e24aeb833fed175a9222.zip | |
fix(ai): count a running sd-cli as the sd engine
fanfictioner runs sd-cli one-shot, not through sd-server, so the sd row
read down while a CLI run held VRAM and generated. A running sd-cli now
reads up and busy, its model marked (sd-cli), and outranks the web UI.
The model is the first --diffusion-model/-m: sd-cli's prompt follows and
is free text that may contain "-m".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rwxr-xr-x | desktop/modules/ai/ai-state.sh | 27 | ||||
| -rwxr-xr-x | desktop/modules/ai/test-ai-state.sh | 12 | ||||
| -rw-r--r-- | docs/superpowers/specs/2026-10-05-ai-module-design.md | 6 |
3 files changed, 37 insertions, 8 deletions
diff --git a/desktop/modules/ai/ai-state.sh b/desktop/modules/ai/ai-state.sh index b3b9642..8b10368 100755 --- a/desktop/modules/ai/ai-state.sh +++ b/desktop/modules/ai/ai-state.sh @@ -15,7 +15,7 @@ # these six lines, tab separated, and exits 0: # # engine<TAB>llama<TAB>up|down<TAB>idle|busy<TAB>model -# engine<TAB>sd<TAB>up|down<TAB>idle|busy<TAB>model +# engine<TAB>sd<TAB>up|down<TAB>idle|busy<TAB>model, "(sd-cli)" for a CLI run # app<TAB>assistant<TAB>running|stopped<TAB>- # app<TAB>chat<TAB>running|stopped<TAB>- # app<TAB>imggen<TAB>running|stopped<TAB>model @@ -50,21 +50,34 @@ llama() { row engine llama up "$busy" "${id:--}" } -sd() { - local line words i model=- busy=idle mtime - line="$(pgrep -axo sd-server)" || { row engine sd down idle -; return; } - read -ra words <<<"$line" +# The basename of the model in a "pid args..." line, or "-". First match +# wins: sd-cli's prompt comes later and is free text that may contain "-m". +model_of() { + local words i + read -ra words <<<"$1" for ((i = 1; i < ${#words[@]} - 1; i++)); do case "${words[i]}" in - --diffusion-model|-m) model="${words[i + 1]##*/}" ;; + --diffusion-model|-m) printf '%s' "${words[i + 1]##*/}"; return ;; esac done + printf - +} + +sd() { + local line busy=idle mtime + # A one-shot sd-cli (fanfictioner runs them) holds VRAM and generates for + # as long as it lives, so it outranks whatever the web UI is doing. + if line="$(pgrep -axo sd-cli)"; then + row engine sd up busy "$(model_of "$line") (sd-cli)" + return + fi + line="$(pgrep -axo sd-server)" || { row engine sd down idle -; return; } # ponytail: sd-server has no progress endpoint, but its log streams # progress bars while generating, so a log written in the last 5s means # busy. A step slower than 5s reads as idle. mtime="$(stat -c %Y "$SD_LOG" 2>/dev/null)" || mtime=0 (( $(date +%s) - mtime < 5 )) && busy=busy - row engine sd up "$busy" "$model" + row engine sd up "$busy" "$(model_of "$line")" } apps() { diff --git a/desktop/modules/ai/test-ai-state.sh b/desktop/modules/ai/test-ai-state.sh index bb6038d..e3e8ba0 100755 --- a/desktop/modules/ai/test-ai-state.sh +++ b/desktop/modules/ai/test-ai-state.sh @@ -51,6 +51,9 @@ STUB cat > "$stub/pgrep" <<'STUB' #!/bin/bash case "$*" in + *sd-cli*) + [[ -n "${T_CLI:-}" ]] || exit 1 + echo "16618 sd-cli --diffusion-model /data/SD/Krea-2-Turbo-Q6_K.gguf --vae /data/SD/vae/wan_2.1_vae.safetensors -p a prompt -m ignored" ;; *sd-server*) [[ -n "${T_SD:-}" ]] || exit 1 echo "4242 sd-server --listen-port 7860 --diffusion-model /data/SD/z_image_turbo-Q8_0.gguf --vae /data/SD/vae/flux1-ae.safetensors" ;; @@ -128,6 +131,15 @@ touch -d '1 minute ago' "$log" got="$(run T_SD=1 | sed -n 2p)" check "sd running, log quiet" "$(l engine sd up idle z_image_turbo-Q8_0.gguf)" "$got" +# A one-shot sd-cli (fanfictioner's) holds VRAM and is generating for as +# long as it runs, whatever the web UI is doing. Its prompt is free text, so +# a "-m" inside it must not be read as the model. +got="$(run T_CLI=1 | sed -n 2p)" +check "sd-cli running" "$(l engine sd up busy "Krea-2-Turbo-Q6_K.gguf (sd-cli)")" "$got" + +got="$(run T_CLI=1 T_SD=1 | sed -n 2p)" +check "sd-cli wins over an idle web UI" "$(l engine sd up busy "Krea-2-Turbo-Q6_K.gguf (sd-cli)")" "$got" + want="$(l app assistant running -; l app chat running -; l app imggen running realvis; l app fanfic running 31337)" got="$(run T_ASSIST=1 T_CHAT=1 T_IMG=1 T_FANFIC=1 | tail -n4)" check "every app running" "$want" "$got" diff --git a/docs/superpowers/specs/2026-10-05-ai-module-design.md b/docs/superpowers/specs/2026-10-05-ai-module-design.md index b1f0f7e..8171f98 100644 --- a/docs/superpowers/specs/2026-10-05-ai-module-design.md +++ b/docs/superpowers/specs/2026-10-05-ai-module-design.md @@ -45,7 +45,11 @@ between the two requests. With the flag an unloaded model answers 400 The engine is down when `/models` does not answer. -**sd.** `pgrep -x sd-server` gives the PID. The model is the basename of the +**sd.** A running `sd-cli` comes first: fanfictioner runs them one-shot, +each holds VRAM and generates for as long as it lives, so the row reads up, +busy, and its model marked `(sd-cli)`. The first version watched only +`sd-server` and showed the engine down during a fanfictioner run. Otherwise +`pgrep -x sd-server` gives the PID. The model is the basename of the value after `--diffusion-model` or `-m` in its arguments. sd-server has no progress endpoint, but its log `~/.cache/sd-server.log` streams progress bars while it generates, so busy means the log was written in the last 5 seconds. |
