Subject-Emu-5259 commited on
Commit
9b3726c
·
verified ·
1 Parent(s): 8bae35c

sync: update docs/INCIDENT-2026-07-14-NEURALAI-PAUSES.md

Browse files
docs/INCIDENT-2026-07-14-NEURALAI-PAUSES.md ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Incident: NeuralAI Service Pauses, "Acting Weird", and Garbage Replies
2
+
3
+ **Date:** 2026-07-14 → 2026-07-15
4
+ **Service:** `neuralai-web-ui` — `https://neuralai-web-ui-deandrewharris.zocomputer.io`
5
+ **Host:** ZO Computer, 4 GB RAM (Free plan can sleep; this incident is mostly separate from sleep)
6
+ **Code:** `file services/webui_service.py`
7
+
8
+ ## TL;DR — What Actually Fixed It
9
+
10
+ The service was paused by the supervisor (`enabled:false` / `paused_at` set), not by OOM.
11
+ Re-enabling + restarting the service, plus the existing `/health` keep-alive ping, stopped the
12
+ pauses. The "acting weird" garbage came from a *brief experiment* where `LLM_BACKEND=zo` was set
13
+ with a short model name, which hit ZO's 402 free-allowance error and fell back into a
14
+ prompt-truncation bug. The correct, deployed backend is `local` — NeuralAI's own
15
+ SmolLM2-360M + NeuralAI LoRA (`checkpoints/v2_model`). That is the product identity; do NOT switch
16
+ it to ZO/HY3 unless you intentionally want HY3 answering instead of NeuralAI.
17
+
18
+ ## Symptoms
19
+
20
+ 1. **Service pauses** — web UI unreachable; supervisor had the service paused/killed.
21
+ 2. **"Acting weird"** — reply to a simple "hey" was a hallucinated, cut-off sentence
22
+ ("That's a great summary! ...while the input_format option is useful for making it easier for
23
+ children to ask questions…"). No relation to the prompt.
24
+ 3. **Short / incoherent replies** during the zo-backend experiment.
25
+
26
+ ## Root Causes (verified)
27
+
28
+ ### 1. Pauses = supervisor pause state, not OOM
29
+
30
+ `list_user_services` showed the service with `enabled:false` / `paused_at` populated. A 503 on the
31
+ liveness probe can also cause the host to pause the service. The `/health` keep-alive pinger
32
+ (`_keep_alive_pinger`, started at boot, `services/webui_service.py:1892`) pings `/health` every
33
+ 5 min to prevent idle sleep. Re-enabling + restarting the service cleared the paused state.
34
+
35
+ > NOTE: An old comment in the code (`webui_service.py:48`) claims "PyTorch + SmolLM2-360M = \~6.2 GB
36
+ > → OOM kill loop." That is inaccurate. SmolLM2-360M-Instruct in float16 is \~720 MB and runs fine on
37
+ > the 4 GB host (verified by the live chat test below). The earlier `[WATCHDOG] Low reclaimable memory` warning was transient. Treat OOM as unlikely for this model; if RAM pressure appears, look
38
+ > at other processes first.
39
+
40
+ ### 2. "Acting weird" = ZO 402 → silent fallback into a prompt-truncation bug
41
+
42
+ During troubleshooting, `LLM_BACKEND=zo` was set with a short model name (`gpt`, `gpt-4o-mini`).
43
+ ZO returned **402** `free_allowance_exhausted` (daily free allowance gone). The streaming path
44
+ caught that and **fell back to the local model** so "chat never breaks" — but the local path had a
45
+ bug:
46
+
47
+ - `build_prompt_with_context` built a **37,907-token** prompt from one oversized chat message.
48
+ - `_truncate_to_fit` then dropped it to **0 turns** (context wiped).
49
+ - `max_input = 768` token cap + 256-token generation → a context-less, incoherent <80-token
50
+ hallucination. That is the "summary/children" reply.
51
+
52
+ ### 3. Short replies
53
+
54
+ Default generation is `max_tokens=256` (`generate_response` / `stream_response`), not 48/80.
55
+ The *garbage* was caused by the 0-turns truncation above, not by token caps.
56
+
57
+ ## Fixes Applied (verified in `file services/webui_service.py`)
58
+
59
+ - `_cap_text(text, max_chars=3500)` added (`:430`) and used inside `build_prompt_with_context`
60
+ (`:451-452`). One oversized paste can no longer explode the prompt to 30k+ tokens and wipe context
61
+ to 0 turns.
62
+ - **Keep-alive pinger started at boot** (`:1892`) — prevents the host from pausing the service on idle.
63
+ - **Memory watchdog started at boot** (`:1893`) — runs GC on low reclaimable memory to avoid OOM kills.
64
+ - `/health` **never 503s** (liveness probe short-circuits before model load, `:872-890`) so the host
65
+ does not pause the service for a slow first load.
66
+ - **No backend change to "fix" the pause.** The correct backend is `local` (NeuralAI's own model).
67
+ The zo-backend experiment was reverted; the service runs `LLM_BACKEND=local`.
68
+
69
+ ## Current Verified Working Config (from `list_user_services`)
70
+
71
+ ```markdown
72
+ LLM_BACKEND = local # NeuralAI's own SmolLM2-360M-Instruct + NeuralAI LoRA (checkpoints/v2_model)
73
+ LLM_MODEL = (empty) # empty → uses BASE_MODEL + MODEL_PATH defaults (the NeuralAI LoRA)
74
+ LLM_API_URL = (empty)
75
+ LLM_API_KEY = (empty)
76
+ GROQ_API_KEY = (empty)
77
+ ```
78
+
79
+ Verified by live chat test: multi-turn replies correctly identify as "NeuralAI built on
80
+ SmolLM2-360M-Instruct with a custom NeuralAI LoRA adapter (SFT v16 + DPO v16)" — i.e. the local
81
+ model is answering, not HY3.
82
+
83
+ ## How To Keep It Stable
84
+
85
+ - **Do NOT let the supervisor pause it.** If the UI goes down, run `update_user_service` with
86
+ `enabled:"true"` and restart. The keep-alive ping handles normal idle sleep.
87
+ - **Keep** `LLM_BACKEND=local` unless you intentionally want HY3 to answer. Switching to `zo` changes
88
+ the model's identity away from NeuralAI.
89
+ - **If you ever use** `zo` **backend**, only use the account's BYOK model ID
90
+ `byok:0d3567f7-f521-42b0-8adf-65c9b036cf89`. Short names (`gpt`, `gpt-4o-mini`, `claude-*`) burn
91
+ the free allowance and 402 → trigger the fallback path that produced the garbage.
92
+ - On chat errors, check `/dev/shm/neuralai-web-ui_err.log`. Errors now surface in the UI instead of
93
+ being masked by a broken fallback reply.
94
+
95
+ ## Verification
96
+
97
+ - `GET /health` → `{"status":"ok","llm_backend":"local","model_status":"ready (external backend)"}`
98
+ (local model loads; status string is cosmetic).
99
+ - Live chat: "hey" → coherent greeting; follow-up "your name is NeuralAI" → correct self-identification
100
+ with model architecture. Multi-turn context preserved. No truncation, no hallucination.
101
+
102
+ ---
103
+
104
+ ## FINAL RESOLUTION (2026-07-15) — SUPERSEDES THE "local is correct" CONCLUSION ABOVE
105
+
106
+ The earlier note claiming the ~6.2 GB RSS was "inaccurate" was itself wrong. Live `free`/`ps`
107
+ measurement showed the **local transformers backend genuinely held ~6.2 GB RSS inside the 4 GB
108
+ sandbox, leaving 0 MB available** → GC thrash → the UI froze/paused under memory pressure. That is a
109
+ real root cause of the pauses, separate from the supervisor-pause state.
110
+
111
+ ### What actually fixed it (verified)
112
+ - **Switched the active backend to llmster** (`LLM_BACKEND=openai_compatible` → `http://localhost:1234/v1`).
113
+ llmster serves `smollm2-360m-instruct` via llama.cpp at **~1.1 GB total** (llama-server 682 MB +
114
+ llmster 416 MB) — versus ~6 GB for local transformers.
115
+ - **RAM after switch: 1.7 GB free** (was 0 MB). Memory-pressure pausing eliminated.
116
+ - **Resilient launcher `run_service.sh`** is now the service entrypoint. At boot it: (1) starts llmster
117
+ headless on :1234, (2) loads the `smollm2-360m-instruct` GGUF, (3) verifies `/v1/models`, and
118
+ (4) only falls back to local PyTorch if llmster is unreachable. This guarantees a live backend
119
+ always exists — no more backend-less 503/401 "model not authed" stalls.
120
+ - **Watchdog + 60 s keep-alive pinger** retained in `webui_service.py`.
121
+ - **Stale env cleared**: `GROQ_API_KEY`, `LLM_API_KEY`, `LLM_API_URL`, and the `zo` backend removed, so
122
+ it never silently depends on a missing key again.
123
+
124
+ ### Verified working state
125
+ ```markdown
126
+ LLM_BACKEND = openai_compatible # -> llmster / LM Studio on http://localhost:1234/v1
127
+ LLM_MODEL = smollm2-360m-instruct
128
+ LLM_API_URL = http://localhost:1234/v1
129
+ LLM_API_KEY = lm-studio
130
+ ```
131
+ - `GET /health` → `{"status":"ready (external backend)","llm_backend":"openai_compatible"}`
132
+ - `POST /api/chat` → streams real tokens from llmster.
133
+ - Public URL `https://neuralai-web-ui-deandrewharris.zocomputer.io` returns 200.
134
+
135
+ ### Note on the 1.4 GB merged model (`Subject-Emu-5259/NeuralAI`)
136
+ The Hugging Face cache only contains a **22 KB stub** for `Subject-Emu-5259/NeuralAI`; the 1.4 GB
137
+ weights are **not downloaded locally**, so it is not in use. The running model is
138
+ `smollm2-360m-instruct` via llmster. To use the merged model, pull the weights (1.4 GB) and load via
139
+ llmster; on this 4 GB sandbox that risks OOM, so a larger plan or a dedicated service is recommended.