learning_ai_common_plat/__LOCAL_LLMs/docs/DASHBOARD_REVIEW.md
saravanakumardb1 093682eace docs(local-llm): add systematic dashboard bug & improvement review
DASHBOARD_REVIEW.md — comprehensive code review of all 6 dashboard files
(1,395 lines). Organized into 7 sections:

- 10 bugs (B1–B10): hardcoded header, blocking pull, escape during stream,
  auto-refresh during streaming, no abort controller, vm_stat page size, etc.
- 5 code quality issues (CQ1–CQ5): monolithic component, inline styles,
  duplicated constants, no error boundary, no loading skeleton
- 16 feature ideas (F1–F16): pull progress, chat mode, prompt history,
  token/s metrics, model search, whisper test, extraction integration, etc.
- 5 performance items (P1–P5): request deduplication, cache TTL, du latency
- 3 security notes (S1–S3): input validation, shell injection pattern, CORS
- Priority matrix and 5-sprint implementation roadmap
2026-02-19 14:36:51 -08:00

14 KiB
Raw Blame History

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

Sprint 1 — Critical fixes (est. 12 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. 23 hrs)

  1. B2/F1 — Streaming pull with progress bar
  2. F6 — Display tokens/second after generation
  3. B10 — Fix brew "Loading..." vs "empty" state

Sprint 3 — Component refactor (est. 23 hrs)

  1. CQ1 — Extract components into separate files
  2. CQ4 — Add Error Boundary
  3. CQ2 — Consolidate inline styles
  4. CQ3 — Shared Ollama URL constant

Sprint 4 — UX enhancements (est. 34 hrs)

  1. F3 — Prompt history (localStorage)
  2. F4 — Chat mode (multi-turn)
  3. F12 — Whisper transcription test
  4. F2 — Model search/filter

Sprint 5 — Integration & polish (est. 23 hrs)

  1. F15 — Extraction service panel
  2. F9 — Modelfile viewer
  3. F7 — Resource trend sparklines
  4. F11 — Keyboard shortcuts panel

Generated by systematic code review of all 6 source files (1,395 lines total).