diff --git a/__LOCAL_LLMs/docs/DASHBOARD_REVIEW.md b/__LOCAL_LLMs/docs/DASHBOARD_REVIEW.md new file mode 100644 index 00000000..36691cb4 --- /dev/null +++ b/__LOCAL_LLMs/docs/DASHBOARD_REVIEW.md @@ -0,0 +1,323 @@ +# Mission Control Dashboard — Bug & Improvement Review + +> Systematic review of `__LOCAL_LLMs/dashboard/` — Feb 19, 2026 + +--- + +## 1. Bugs + +### B1. Hardcoded "Apple M4 Pro · 48 GB" in header + +**File:** `page.tsx:317` +**Severity:** Medium +**Issue:** Header subtitle is hardcoded: `Apple M4 Pro · 48 GB · {system?.platform}`. Should use `system?.chip` and `system?.memory.total` dynamically so it works on any machine. + +### B2. Pull model blocks entire UI with `stream: false` + +**File:** `api/ollama/route.ts:88` +**Severity:** High +**Issue:** `handlePull` calls the Ollama pull API with `stream: false`, which means the HTTP request blocks until the entire download completes. For large models (e.g., 20 GB), this can take 30+ minutes. The Next.js API route will likely timeout, and the user has no progress feedback. +**Fix:** Use `stream: true` for pull and pipe progress events to the client (like the generate stream endpoint). Show download percentage in the UI. + +### B3. Generate (non-streaming) endpoint still exists but is unused + +**File:** `api/ollama/route.ts:69-82` +**Severity:** Low +**Issue:** The `action === 'generate'` block in the POST handler is dead code — the UI exclusively uses the `/api/ollama/stream` endpoint for prompts. Dead code adds maintenance burden. + +### B4. Escape key closes prompt modal even while streaming + +**File:** `page.tsx:188-197` +**Severity:** Medium +**Issue:** The global Escape keydown handler calls `setPromptModel(null)` unconditionally, but the backdrop click handler correctly checks `!promptLoading` before closing. Pressing Escape during an active stream will close the modal and discard the response. The Escape handler should respect `promptLoading` state too. + +### B5. Auto-refresh (15s) during active streaming + +**File:** `page.tsx:182-185` +**Severity:** Medium +**Issue:** The `setInterval(fetchAll, 15000)` runs unconditionally, including while a prompt is streaming. This causes background network churn and state updates (flicker) while the user is reading a streaming response. Should pause auto-refresh while `promptLoading` or `pullLoading` is true. + +### B6. Toast ID uses incrementing ref (not globally unique) + +**File:** `page.tsx:156-159` +**Severity:** Low +**Issue:** `toastId.current` is a simple incrementing number. If the component remounts (e.g., HMR during dev), it resets to 0, potentially creating duplicate IDs. Not a production risk but can cause React key warnings during development. + +### B7. `vm_stat` page size is hardcoded to 16384 + +**File:** `api/system/route.ts:103` +**Severity:** Low +**Issue:** Page size is hardcoded as `16384` (Apple Silicon default). The `vm_stat` output actually prints the page size on its first line: `"Mach Virtual Memory Statistics: (page size of 16384 bytes)"`. Should parse it from the output for correctness on non-standard configurations. + +### B8. Whisper models directory hardcoded to `~/whisper-models` + +**File:** `api/whisper/route.ts:24` +**Severity:** Medium +**Issue:** The models directory is hardcoded to `~/whisper-models`. Different users or Homebrew versions may store GGML models elsewhere (e.g., `/opt/homebrew/share/whisper-cpp/models/`). Should check multiple known paths or make it configurable via env var. + +### B9. No abort controller for streaming fetch + +**File:** `page.tsx:250-289` +**Severity:** Medium +**Issue:** When the user closes the prompt modal during streaming, the `reader.read()` loop continues running in the background. There's no `AbortController` to cancel the fetch. This wastes bandwidth and CPU until the model finishes generating. + +### B10. Brew packages shows "Loading..." when none are installed + +**File:** `page.tsx:936-940` +**Severity:** Low +**Issue:** When `system.brewPackages` is an empty array (all 3 packages not installed), the UI shows "Loading..." instead of "No packages found". The condition checks `length === 0` but doesn't distinguish between "still loading" and "loaded but empty". + +--- + +## 2. Code Quality Issues + +### CQ1. 1079-line single component (page.tsx) + +**Severity:** Medium +**Issue:** The entire dashboard is a single `Dashboard` component in one file. It contains interfaces, utility functions, sub-components (`StatusDot`, `ProgressBar`), and 900+ lines of JSX. This makes it hard to read, test, or extend. +**Fix:** Extract into: + +- `components/StatusDot.tsx` +- `components/ProgressBar.tsx` +- `components/ToastContainer.tsx` +- `components/PromptModal.tsx` +- `components/OllamaModelsPanel.tsx` +- `components/SystemPanel.tsx` +- `components/WhisperPanel.tsx` +- `components/BrewPanel.tsx` +- `lib/types.ts` (interfaces) +- `lib/format.ts` (formatBytes, formatUptime) + +### CQ2. Inline styles everywhere instead of CSS classes + +**Severity:** Low +**Issue:** Most color/background styling is done via inline `style={{ color: 'var(--text-tertiary)' }}` rather than Tailwind utilities or CSS classes. This adds JSX noise and makes theming harder. +**Fix:** Define utility classes in `globals.css` (e.g., `.text-muted { color: var(--text-tertiary); }`) or use Tailwind's `text-[var(--text-tertiary)]` syntax. + +### CQ3. OLLAMA_URL duplicated across two files + +**File:** `api/ollama/route.ts:3`, `api/ollama/stream/route.ts:3` +**Severity:** Low +**Issue:** The Ollama URL default is defined independently in both files. Should be a shared constant or env utility. + +### CQ4. No error boundaries + +**Severity:** Medium +**Issue:** If any component throws during render (e.g., unexpected API response shape), the entire dashboard crashes with an unhandled error. Should add a React Error Boundary for graceful degradation. + +### CQ5. No loading skeleton + +**Severity:** Low +**Issue:** On initial load, the dashboard shows empty cards with "..." values. A skeleton/shimmer UI would look more polished. + +--- + +## 3. Feature Improvements + +### F1. Pull progress streaming + +**Priority:** High +**Description:** Stream model pull progress from Ollama (`stream: true` on `/api/pull`) and show a real-time progress bar with download percentage, speed, and ETA. Current pull just shows a toast and blocks. + +### F2. Model search/filter + +**Priority:** Medium +**Description:** When many models are installed (10+), add a search/filter input above the models list. Filter by name, family, or quantization level. + +### F3. Prompt history + +**Priority:** Medium +**Description:** Store recent prompts in `localStorage` and show a dropdown/list in the prompt modal. Allow re-running previous prompts with one click. + +### F4. Chat mode (multi-turn) + +**Priority:** Medium +**Description:** Current prompt modal is single-shot. Add chat mode with conversation history, using Ollama's `/api/chat` endpoint instead of `/api/generate`. Show messages in a chat bubble layout. + +### F5. Model comparison + +**Priority:** Low +**Description:** Side-by-side prompt comparison: send the same prompt to 2 models simultaneously and show both responses for latency/quality comparison. + +### F6. Token/s metrics display + +**Priority:** Medium +**Description:** The streaming endpoint already receives `eval_count` and `eval_duration` in the final NDJSON chunk. Parse and display tokens/second after generation completes. Already available in the non-streaming generate response but not surfaced in UI. + +### F7. System resource charts (time series) + +**Priority:** Medium +**Description:** Store memory/CPU snapshots over time (in-memory ring buffer or localStorage) and render a mini sparkline chart showing resource usage trends. Helpful for spotting memory leaks from loaded models. + +### F8. Ollama server logs viewer + +**Priority:** Low +**Description:** Read Ollama server logs (stdout/stderr or `~/.ollama/logs/`) and display in a collapsible panel. Useful for debugging model loading issues. + +### F9. Model Modelfile viewer + +**Priority:** Low +**Description:** The `show` action already fetches model details including the Modelfile. Add a collapsible code block in the expanded model details showing the full Modelfile, template, and system prompt. + +### F10. Dark/light theme toggle + +**Priority:** Low +**Description:** Current dashboard is dark-only. Add a theme toggle storing preference in localStorage. The CSS variables architecture already supports it — just need a `:root.light` override. + +### F11. Keyboard shortcuts panel + +**Priority:** Low +**Description:** Add a `?` keyboard shortcut to show all available shortcuts. Currently only Cmd+Enter (send) and Escape (close) exist but aren't discoverable. + +### F12. Whisper transcription test + +**Priority:** Medium +**Description:** Add a "Test" button in the Whisper panel that lets users upload/record a short audio clip and transcribe it locally. Shows real latency and accuracy. + +### F13. Responsive / mobile layout + +**Priority:** Low +**Description:** The grid layout works but isn't optimized for mobile. The 4-column stats row and 3-column main grid could use better breakpoints and a hamburger menu for mobile. + +### F14. Model tags/labels + +**Priority:** Low +**Description:** Let users tag models (e.g., "coding", "fast", "vision") with colored labels for quick visual identification. Store in localStorage. + +### F15. Extraction service integration + +**Priority:** Medium +**Description:** Add a panel showing extraction-service status (port 4005), active tasks, and a button to run test extractions against loaded Ollama models. This connects the dashboard to the actual LysnrAI pipeline. + +### F16. Auto-load preferred model on Ollama start + +**Priority:** Low +**Description:** Let users mark a model as "auto-load". When the dashboard detects Ollama is online but no models are loaded, automatically load the preferred model. + +--- + +## 4. Performance & Reliability + +### P1. No request deduplication + +**Severity:** Medium +**Issue:** Rapid clicks on Refresh or model actions can fire duplicate requests. Add request deduplication or disable buttons during pending requests (partially done for `actionLoading` but not for `fetchAll`). + +### P2. Static cache never invalidates + +**File:** `api/system/route.ts:81-90` +**Severity:** Low +**Issue:** `staticCache` (chip, GPU, brew packages) is cached indefinitely per server process. If the user installs/upgrades a brew package while the dashboard is running, it won't reflect. Add a TTL (e.g., 5 minutes) or a manual "Refresh system info" button. + +### P3. `du -sk ~/.ollama/models` can be slow + +**File:** `api/system/route.ts:41` +**Severity:** Low +**Issue:** `du` traverses the entire models directory on every system API call (every 15s refresh). For large model collections, this can take seconds. Should cache with TTL (e.g., 60s). + +### P4. No connection timeout on Ollama fetch + +**File:** `api/ollama/route.ts:5-12` +**Severity:** Medium +**Issue:** `fetchOllama` has no `signal` (AbortController) or timeout. If Ollama hangs or is unreachable, the request hangs indefinitely. Add a 5-second timeout. + +### P5. `system_profiler SPDisplaysDataType` is slow (~2-3s) + +**File:** `api/system/route.ts:52-53` +**Severity:** Low (cached) +**Issue:** This command is slow but cached on first call. However, the first dashboard load still waits for it. Consider running it lazily or returning a placeholder while it loads. + +--- + +## 5. Security & Hardening + +### S1. No input validation on model names + +**File:** `api/ollama/route.ts:50-51` +**Severity:** Medium +**Issue:** The `model` field from the request body is passed directly to Ollama API calls without validation. While Ollama itself validates, the dashboard should sanitize to prevent unexpected payloads (e.g., very long strings, special characters). + +### S2. Shell injection via brew package names + +**File:** `api/system/route.ts:67` +**Severity:** Low (hardcoded targets) +**Issue:** The `targets` array is hardcoded so this isn't exploitable now, but the `execAsync` pattern of interpolating into shell commands is risky if the targets list ever becomes dynamic. Use `execFile` instead of `exec` for safety. + +### S3. No CORS or auth + +**Severity:** Low (local-only) +**Issue:** The dashboard has no authentication or CORS restrictions. Any local process or browser tab can call the API routes. Acceptable for a local dev tool but worth noting. + +--- + +## 6. Priority Matrix + +| ID | Type | Priority | Effort | Impact | +| ---------- | ------- | -------- | ------- | ------ | +| B2 | Bug | P0 | Medium | High | +| B4 | Bug | P0 | Low | Medium | +| B5 | Bug | P1 | Low | Medium | +| B9 | Bug | P1 | Low | Medium | +| B1 | Bug | P1 | Low | Low | +| P4 | Perf | P1 | Low | Medium | +| CQ1 | Quality | P1 | High | High | +| F1 | Feature | P1 | Medium | High | +| F6 | Feature | P1 | Low | Medium | +| CQ4 | Quality | P2 | Low | Medium | +| F3 | Feature | P2 | Medium | Medium | +| F4 | Feature | P2 | High | High | +| F12 | Feature | P2 | Medium | Medium | +| F15 | Feature | P2 | Medium | Medium | +| B8 | Bug | P2 | Low | Low | +| B10 | Bug | P2 | Low | Low | +| P1 | Perf | P2 | Low | Low | +| P2 | Perf | P2 | Low | Low | +| P3 | Perf | P2 | Low | Low | +| F2 | Feature | P3 | Low | Medium | +| F7 | Feature | P3 | Medium | Medium | +| F9 | Feature | P3 | Low | Low | +| F11 | Feature | P3 | Low | Low | +| All others | Various | P3+ | Various | Low | + +--- + +## 7. Recommended Implementation Order + +### Sprint 1 — Critical fixes (est. 1–2 hrs) + +1. **B4** — Guard Escape key during streaming +2. **B5** — Pause auto-refresh during prompt/pull +3. **B9** — Add AbortController to streaming fetch +4. **B1** — Dynamic chip/RAM in header +5. **P4** — Add timeout to Ollama fetch calls + +### Sprint 2 — Pull progress + metrics (est. 2–3 hrs) + +6. **B2/F1** — Streaming pull with progress bar +7. **F6** — Display tokens/second after generation +8. **B10** — Fix brew "Loading..." vs "empty" state + +### Sprint 3 — Component refactor (est. 2–3 hrs) + +9. **CQ1** — Extract components into separate files +10. **CQ4** — Add Error Boundary +11. **CQ2** — Consolidate inline styles +12. **CQ3** — Shared Ollama URL constant + +### Sprint 4 — UX enhancements (est. 3–4 hrs) + +13. **F3** — Prompt history (localStorage) +14. **F4** — Chat mode (multi-turn) +15. **F12** — Whisper transcription test +16. **F2** — Model search/filter + +### Sprint 5 — Integration & polish (est. 2–3 hrs) + +17. **F15** — Extraction service panel +18. **F9** — Modelfile viewer +19. **F7** — Resource trend sparklines +20. **F11** — Keyboard shortcuts panel + +--- + +_Generated by systematic code review of all 6 source files (1,395 lines total)._