docs(roadmap): make Local AI consolidation roadmap execution-ready
- Add master Execution Checklist at top with all 4 phases as checkbox items - Add Safety Matrix (Delete vs Keep) with verified import analysis - Convert all phase steps to checkbox format with COMMIT markers - Move Phase 4 from after Success Criteria to between Phase 3 and File Inventory - Add pre-flight baseline check (step 1.0) - Add pnpm install step after dep removal (step 1.6b) - Add grep verification step to catch stale imports (step 1.5) - Split E2E updates into separate commit (step 1.8) - Add .env.example updates for Phase 2 env vars - Clarify types.ts trim: move Attachment+ModelDefaults then delete (not partial keep) - Phase 3 steps now have individual COMMIT messages - All 4 phases now grouped together in the Phases section
This commit is contained in:
parent
808f405889
commit
93e6d0b9b7
@ -27,6 +27,127 @@ Both repos have a **chat workspace** that talks to Ollama, stores conversations,
|
||||
|
||||
---
|
||||
|
||||
## Execution Checklist
|
||||
|
||||
> **Track progress here.** Check each box as you go. Each `COMMIT` line is a commit point — verify before committing.
|
||||
> Update the `Status` header at the top of this file as you progress.
|
||||
|
||||
### Phase 1 — Remove Workspace from LLM Lab (~2 hours)
|
||||
|
||||
- [ ] **1.0** Pre-flight: `cd dashboard && pnpm test && pnpm run build` (establish green baseline)
|
||||
- [ ] **1.1** Delete `dashboard/src/app/(workspace)/` directory (22 files: 6 routes + 16 components)
|
||||
- [ ] **1.2** Delete `dashboard/src/app/api/ollama/title/` directory (1 workspace-only route)
|
||||
- [ ] **1.3** Delete workspace lib files: `db.ts`, `agents.ts`, `quick-actions.ts`, `scheduled-tasks.ts`, `cron.ts`, `migrate.ts`
|
||||
- [ ] **1.3a** Trim `types.ts`: move `Attachment` + `ModelDefaults` to shared section (lines 1–77), then delete everything from line 78 onward
|
||||
- [ ] **1.4** Delete workspace tests: `__tests__/agents.test.ts`, `__tests__/cron.test.ts`
|
||||
- [ ] **1.5** Create `dashboard/src/app/page.tsx` → `redirect('/mission-control')`
|
||||
- [ ] **1.6a** Remove npm deps from `package.json`: `idb`, `cron-parser`, `fuse.js`
|
||||
- [ ] **1.6b** Run `pnpm install` to update lockfile
|
||||
- [ ] **1.7** Verify: `pnpm run typecheck && pnpm test && pnpm run build`
|
||||
- [ ] COMMIT → `refactor(dashboard): remove chat workspace — LocalMemGPT is the chat surface`
|
||||
- [ ] **1.8a** Delete `e2e/chat.spec.ts`
|
||||
- [ ] **1.8b** Update `e2e/health.spec.ts` — remove `/c/test-id` test
|
||||
- [ ] **1.8c** Update `e2e/accessibility.spec.ts` — remove `/compare`, `/tts`, `/whisper` from routes
|
||||
- [ ] **1.8d** Update `e2e/visual-regression.spec.ts` — remove `workspace` and `compare` from ROUTES
|
||||
- [ ] **1.8e** Update `e2e/navigation.spec.ts` — `/` now redirects to `/mission-control`
|
||||
- [ ] **1.8f** Update `e2e/models.spec.ts` — `/` now redirects
|
||||
- [ ] COMMIT → `test(e2e): update specs after workspace removal`
|
||||
- [ ] **1.9** Update `AGENTS.md`, `README.md`, `CLAUDE.md`, `.windsurfrules`, `.cursorrules`
|
||||
- [ ] COMMIT → `docs: update agent docs after workspace removal`
|
||||
|
||||
### Phase 2 — Cross-Links (~30 minutes)
|
||||
|
||||
- [ ] **2.1** Add "Chat with models →" card/banner in Mission Control (LLM Lab repo, env: `LOCALMEMGPT_URL`)
|
||||
- [ ] **2.1a** Add `LOCALMEMGPT_URL` to `.env.example`
|
||||
- [ ] COMMIT → `feat(dashboard): add LocalMemGPT cross-link in Mission Control`
|
||||
- [ ] **2.2** Add "Model Management →" link in Settings panel (LocalMemGPT repo, env: `LOCALLLMLAB_URL`)
|
||||
- [ ] **2.2a** Add `LOCALLLMLAB_URL` to `.env.example`
|
||||
- [ ] COMMIT → `feat(web): add LLM Lab cross-link in settings`
|
||||
|
||||
### Phase 3 (Optional) — Port Features to LocalMemGPT
|
||||
|
||||
> Only after Phase 1+2 are complete and you've identified what's actually missed.
|
||||
|
||||
- [ ] **3.1** Conversation Attachments (P1, ~1 day)
|
||||
- [ ] **3.2** Model Ratings (P1, ~2 hours)
|
||||
- [ ] **3.3** Projects / Folders (P1, ~4 hours)
|
||||
- [ ] **3.4** Custom Agents (P2, ~4 hours)
|
||||
- [ ] **3.5** Quick Actions (P2, ~6 hours)
|
||||
- [ ] **3.6** Voice Input — Whisper STT (P2, ~4 hours)
|
||||
- [ ] **3.7** Orchestrations (P3, ~1 day)
|
||||
- [ ] **3.8** Scheduled Tasks (P3, ~4 hours)
|
||||
|
||||
### Phase 4 — DRY Extractions (~3–5 days, independent of Phases 1–3)
|
||||
|
||||
- [ ] **4.1** Create `@bytelyst/ollama-client` package in `learning_ai_common_plat/packages/`
|
||||
- [ ] **4.1a** Migrate `learning_ai_local_memory_gpt/backend/src/lib/ollama.ts` → import from package
|
||||
- [ ] **4.1b** Simplify LLM Lab `api/ollama/*` routes + `ollama-config.ts` → import from package
|
||||
- [ ] **4.1c** Publish to Gitea registry
|
||||
- [ ] COMMIT → `feat(ollama-client): shared Ollama API client package`
|
||||
- [ ] **4.2** Add per-request helpers to `@bytelyst/fastify-sse` (startSSE, sendSSEData, endSSE)
|
||||
- [ ] **4.2a** Migrate LocalMemGPT `lib/sse-helpers.ts` → import from package
|
||||
- [ ] COMMIT → `feat(fastify-sse): add per-request SSE helpers`
|
||||
- [ ] **4.3** Create `@bytelyst/use-theme` package (configurable storage key + DOM strategy)
|
||||
- [ ] **4.3a** Migrate 6 web apps to import from package
|
||||
- [ ] COMMIT → `feat(use-theme): shared React theme toggle hook`
|
||||
- [ ] **4.4** Create `@bytelyst/use-keyboard-shortcuts` package (data-driven ShortcutDef[])
|
||||
- [ ] **4.4a** Migrate 5 web apps to import from package
|
||||
- [ ] COMMIT → `feat(use-keyboard-shortcuts): shared React keyboard shortcuts hook`
|
||||
|
||||
---
|
||||
|
||||
## Safety Matrix — Delete vs Keep
|
||||
|
||||
> **Verified by import analysis.** No Mission Control or API route code imports any file in the DELETE column.
|
||||
|
||||
### Files to DELETE (workspace-only)
|
||||
|
||||
| File / Directory | Verified By |
|
||||
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `(workspace)/` — 6 routes + 16 components (22 files) | No imports from `(mission-control)/` or `api/` |
|
||||
| `api/ollama/title/` — 1 route | Only imported by `ConversationView.tsx` (deleted) |
|
||||
| `lib/db.ts` — IndexedDB client | Only imported by workspace components |
|
||||
| `lib/agents.ts` — agent definitions | Only imported by workspace components |
|
||||
| `lib/quick-actions.ts` — quick action defs | Only imported by workspace components |
|
||||
| `lib/scheduled-tasks.ts` — task scheduler | Only imported by workspace components |
|
||||
| `lib/cron.ts` — cron parser | Only imported by `TaskEditor.tsx`, `TaskRunner.tsx` (deleted) |
|
||||
| `lib/migrate.ts` — IndexedDB migration | Only imported by `db.ts` (deleted) |
|
||||
| `types.ts` lines 78–118, 127–162, 170–221 | Workspace types: Conversation, Message, Agent, AgentTool, QuickAction, Project, ScheduledTask, Orchestration |
|
||||
| `__tests__/agents.test.ts`, `__tests__/cron.test.ts` | Test deleted lib files |
|
||||
| `e2e/chat.spec.ts` | Tests workspace chat flow |
|
||||
| npm deps: `idb`, `cron-parser`, `fuse.js` | Only used by deleted files |
|
||||
|
||||
### Files to KEEP (Mission Control + shared infrastructure)
|
||||
|
||||
| File / Directory | Used By (verified) |
|
||||
| ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
|
||||
| `(mission-control)/` — page.tsx + components | Core product — model ops, benchmarks, hardware |
|
||||
| `api/ollama/route.ts` — model list/pull/delete | Mission Control model management |
|
||||
| `api/ollama/pull/` — pull with progress | Mission Control model pull UI |
|
||||
| `api/ollama/chat/` — chat proxy | Mission Control model testing (page.tsx:719) |
|
||||
| `api/ollama/stream/` — streaming proxy | Mission Control comparison/benchmarks (page.tsx:331,538) |
|
||||
| `api/ollama/logs/` — log viewer | Mission Control log viewer (page.tsx:297) |
|
||||
| `api/tts/` — TTS engine | Mission Control TTS panel (page.tsx:179) |
|
||||
| `api/whisper/` — Whisper status | Mission Control Whisper card (page.tsx:165) |
|
||||
| `api/whisper/transcribe/` — transcription test | Mission Control test button (page.tsx:313) |
|
||||
| `api/system/`, `system/memory/`, `system/exec/` | Mission Control hardware stats |
|
||||
| `api/health/`, `api/extraction/` | Infrastructure |
|
||||
| `lib/llm-router.ts` | `api/ollama/chat/route.ts` |
|
||||
| `lib/ollama-config.ts` | All `api/ollama/*` routes |
|
||||
| `lib/product-config.ts` | Root layout |
|
||||
| `lib/router.ts` | `api/ollama/chat/route.ts` (imports Attachment + ModelDefaults) |
|
||||
| `lib/format.ts` | Mission Control page.tsx + `RamBudgetBar.tsx` |
|
||||
| `lib/use-theme.ts` | Mission Control page.tsx |
|
||||
| `lib/use-keyboard-shortcuts.ts` | Shared keyboard shortcuts |
|
||||
| `types.ts` lines 1–77 + Attachment (119–126) + ModelDefaults (163–169) | Mission Control types + `router.ts` dependency chain |
|
||||
| `src/components/` — 6 shared components | Mission Control page.tsx imports all 6 |
|
||||
| `__tests__/` — 5 test files (format, ollama-config, product-config, use-keyboard-shortcuts, use-theme) | Tests for kept lib files |
|
||||
| `e2e/` — 6 specs (updated, not deleted) | Mission Control + navigation + accessibility |
|
||||
| `globals.css` | All shared styles (verified: no workspace-specific tokens) |
|
||||
| `docs/`, `tts/`, `scripts/`, `experiments/`, `chat-history/` | Non-dashboard assets |
|
||||
|
||||
---
|
||||
|
||||
## Review Findings
|
||||
|
||||
> **Pass 1:** 13 bugs/gaps identified during systematic codebase verification (2026-03-29).
|
||||
@ -216,15 +337,15 @@ These are **optional ports** — only if they prove valuable:
|
||||
|
||||
#### 1.1 Delete workspace route group
|
||||
|
||||
Delete the entire `(workspace)/` directory:
|
||||
- [ ] Delete the entire `(workspace)/` directory:
|
||||
|
||||
```
|
||||
dashboard/src/app/(workspace)/ # 22 items — layout, pages, 16 components
|
||||
dashboard/src/app/(workspace)/ # 22 files — layout, 5 pages, 16 components
|
||||
```
|
||||
|
||||
#### 1.2 Delete workspace-specific API routes
|
||||
|
||||
Delete **only** the route that is workspace-only:
|
||||
- [ ] Delete **only** the route that is workspace-only:
|
||||
|
||||
```
|
||||
dashboard/src/app/api/ollama/title/ # Auto-title generation (workspace-only)
|
||||
@ -241,7 +362,7 @@ dashboard/src/app/api/ollama/title/ # Auto-title generation (workspace-only)
|
||||
|
||||
#### 1.3 Clean up lib files
|
||||
|
||||
Delete workspace-only lib files:
|
||||
- [ ] Delete workspace-only lib files:
|
||||
|
||||
```
|
||||
dashboard/src/app/lib/db.ts # IndexedDB (conversations, messages, agents, etc.)
|
||||
@ -252,10 +373,16 @@ dashboard/src/app/lib/cron.ts # Cron parser
|
||||
dashboard/src/app/lib/migrate.ts # v3→v4 migration
|
||||
```
|
||||
|
||||
Remove workspace types from `types.ts` (lines 78–221: Conversation, Message, Agent, QuickAction, Project, ScheduledTask, Orchestration). Keep lines 1–77 (OllamaModel, RunningModel, OllamaData, WhisperModel, WhisperData, SystemData, Toast, PullProgress, StreamMetrics). **[F9, R8] Also keep `Attachment` (lines 119–126) and `ModelDefaults` (lines 163–169)** — both are imported by `router.ts` (line 1: `import type { Attachment, ModelDefaults } from './types'`) which is used by `api/ollama/chat/route.ts` (shared).
|
||||
- [ ] Trim `types.ts` — **[F9, R8] do this carefully:**
|
||||
1. Copy `Attachment` (lines 119–126) and `ModelDefaults` (lines 163–169) into the shared section (after line 77)
|
||||
2. Delete everything from the original line 78 (`// --- v4 Workspace Types ---`) to end of file
|
||||
3. **Why:** `router.ts` line 1 does `import type { Attachment, ModelDefaults } from './types'` → used by `api/ollama/chat/route.ts` (Mission Control)
|
||||
4. **Verify:** `grep -r 'from.*types' dashboard/src/app/lib/router.ts` should still resolve
|
||||
|
||||
#### 1.4 Delete workspace-specific tests
|
||||
|
||||
- [ ] Delete workspace test files:
|
||||
|
||||
```
|
||||
dashboard/src/app/lib/__tests__/agents.test.ts
|
||||
dashboard/src/app/lib/__tests__/cron.test.ts
|
||||
@ -265,7 +392,7 @@ dashboard/src/app/lib/__tests__/cron.test.ts
|
||||
|
||||
**[F3] CRITICAL:** The workspace route group `(workspace)/page.tsx` is the current root page (`/`). There is no separate `src/app/page.tsx`. After deleting the workspace, `GET /` returns 404.
|
||||
|
||||
**Required:** Create `dashboard/src/app/page.tsx` that redirects to `/mission-control`:
|
||||
- [ ] Create `dashboard/src/app/page.tsx` that redirects to `/mission-control`:
|
||||
|
||||
```tsx
|
||||
import { redirect } from 'next/navigation';
|
||||
@ -274,8 +401,8 @@ export default function Home() {
|
||||
}
|
||||
```
|
||||
|
||||
- Verify `layout.tsx` root has no workspace-specific imports (confirmed: it only imports fonts, design tokens, product-config, and Providers — no workspace references)
|
||||
- Update any shared components that reference workspace routes
|
||||
- [ ] Verify `layout.tsx` root has no workspace-specific imports (confirmed: it only imports fonts, design tokens, product-config, and Providers — no workspace references)
|
||||
- [ ] Grep for any remaining imports of deleted files: `grep -r 'from.*\(workspace\)\|from.*db\|from.*agents\|from.*quick-actions\|from.*scheduled-tasks\|from.*cron\|from.*migrate' dashboard/src/`
|
||||
|
||||
#### 1.6 ~~Update globals.css~~ — NO ACTION NEEDED [F8]
|
||||
|
||||
@ -283,27 +410,38 @@ export default function Home() {
|
||||
|
||||
#### 1.6a Remove workspace-only npm dependencies [F7]
|
||||
|
||||
Remove from `dashboard/package.json`:
|
||||
|
||||
- `idb` — IndexedDB client, only used by `db.ts` (workspace)
|
||||
- `cron-parser` — only used by `cron.ts` and `TaskEditor.tsx` / `TaskRunner.tsx` (workspace)
|
||||
- `fuse.js` — only used by `CommandPalette.tsx` (workspace)
|
||||
- [ ] Remove from `dashboard/package.json`:
|
||||
- `idb` — IndexedDB client, only used by `db.ts` (workspace)
|
||||
- `cron-parser` — only used by `cron.ts` and `TaskEditor.tsx` / `TaskRunner.tsx` (workspace)
|
||||
- `fuse.js` — only used by `CommandPalette.tsx` (workspace)
|
||||
- [ ] Run `pnpm install` to update lockfile
|
||||
|
||||
#### 1.7 Verify
|
||||
|
||||
- [ ] Run and confirm all pass:
|
||||
|
||||
```bash
|
||||
cd dashboard && pnpm run typecheck && pnpm test && pnpm run build
|
||||
```
|
||||
|
||||
**Commit:** `refactor(dashboard): remove chat workspace — LocalMemGPT is the chat surface`
|
||||
- [ ] **COMMIT:** `refactor(dashboard): remove chat workspace — LocalMemGPT is the chat surface`
|
||||
|
||||
#### 1.8 Update documentation
|
||||
#### 1.8 Update E2E specs
|
||||
|
||||
- Update `AGENTS.md` — remove workspace references, update repo layout, adjust test count
|
||||
- Update `README.md` — remove chat workspace description, add note pointing to Local Memory GPT for chat
|
||||
- Update `CLAUDE.md`, `.windsurfrules`, `.cursorrules` — align with new scope
|
||||
- [ ] Delete `e2e/chat.spec.ts` — workspace chat flow
|
||||
- [ ] Update `e2e/health.spec.ts` — remove `Conversation Routes` test navigating to `/c/test-id`
|
||||
- [ ] Update `e2e/accessibility.spec.ts` — remove `/compare`, `/tts`, `/whisper` from routes array
|
||||
- [ ] Update `e2e/visual-regression.spec.ts` — remove `workspace` and `compare` from ROUTES
|
||||
- [ ] Update `e2e/navigation.spec.ts` — `/` now redirects to `/mission-control`
|
||||
- [ ] Update `e2e/models.spec.ts` — `/` now redirects
|
||||
- [ ] **COMMIT:** `test(e2e): update specs after workspace removal`
|
||||
|
||||
**Commit:** `docs: update agent docs after workspace removal`
|
||||
#### 1.9 Update documentation
|
||||
|
||||
- [ ] Update `AGENTS.md` — remove workspace references, update repo layout, adjust test count
|
||||
- [ ] Update `README.md` — remove chat workspace description, add note pointing to Local Memory GPT for chat
|
||||
- [ ] Update `CLAUDE.md`, `.windsurfrules`, `.cursorrules` — align with new scope
|
||||
- [ ] **COMMIT:** `docs: update agent docs after workspace removal`
|
||||
|
||||
---
|
||||
|
||||
@ -315,22 +453,20 @@ cd dashboard && pnpm run typecheck && pnpm test && pnpm run build
|
||||
|
||||
#### 2.1 Local LLM Lab → Local Memory GPT link
|
||||
|
||||
Add a prominent link in Mission Control dashboard pointing to Local Memory GPT:
|
||||
|
||||
- "Chat with your models →" card or banner
|
||||
- Links to `http://localhost:3070` (LocalMemGPT web default port)
|
||||
- Configurable via env var: `LOCALMEMGPT_URL`
|
||||
|
||||
**Commit:** `feat(dashboard): add LocalMemGPT cross-link in Mission Control`
|
||||
- [ ] Add a prominent link in Mission Control dashboard pointing to Local Memory GPT:
|
||||
- "Chat with your models →" card or banner
|
||||
- Links to `http://localhost:3070` (LocalMemGPT web default port)
|
||||
- Configurable via env var: `LOCALMEMGPT_URL`
|
||||
- [ ] Add `LOCALMEMGPT_URL` to `.env.example`
|
||||
- [ ] **COMMIT:** `feat(dashboard): add LocalMemGPT cross-link in Mission Control`
|
||||
|
||||
#### 2.2 Local Memory GPT → Local LLM Lab link
|
||||
|
||||
Add a link in Settings panel:
|
||||
|
||||
- "Model Management →" pointing to `http://localhost:3000` (LLM Lab dashboard)
|
||||
- Configurable via env var: `LOCALLLMLAB_URL`
|
||||
|
||||
**Commit:** `feat(web): add LLM Lab cross-link in settings`
|
||||
- [ ] Add a link in Settings panel:
|
||||
- "Model Management →" pointing to `http://localhost:3000` (LLM Lab dashboard)
|
||||
- Configurable via env var: `LOCALLLMLAB_URL`
|
||||
- [ ] Add `LOCALLLMLAB_URL` to `.env.example`
|
||||
- [ ] **COMMIT:** `feat(web): add LLM Lab cross-link in settings`
|
||||
|
||||
---
|
||||
|
||||
@ -346,10 +482,11 @@ Only pursue these if the features are missed after Phase 1.
|
||||
|
||||
LocalMemGPT currently has no attachment support. LLM Lab supports image/file/audio/url attachments.
|
||||
|
||||
- Add `attachments` field to messages table (SQLite JSON column)
|
||||
- Update chat SSE handler to pass images to Ollama vision models
|
||||
- Update `MessageBubble.tsx` to render attachments
|
||||
- Update `ChatView.tsx` input to support file upload
|
||||
- [ ] Add `attachments` field to messages table (SQLite JSON column)
|
||||
- [ ] Update chat SSE handler to pass images to Ollama vision models
|
||||
- [ ] Update `MessageBubble.tsx` to render attachments
|
||||
- [ ] Update `ChatView.tsx` input to support file upload
|
||||
- [ ] **COMMIT:** `feat(chat): add attachment support for multimodal input`
|
||||
|
||||
**Effort:** ~1 day
|
||||
|
||||
@ -357,9 +494,10 @@ LocalMemGPT currently has no attachment support. LLM Lab supports image/file/aud
|
||||
|
||||
LLM Lab has per-message thumbs up/down ratings with IndexedDB storage.
|
||||
|
||||
- Add `rating` column to messages table
|
||||
- Add `PATCH /api/messages/:id/rate` endpoint
|
||||
- Update `MessageBubble.tsx` with rating buttons
|
||||
- [ ] Add `rating` column to messages table
|
||||
- [ ] Add `PATCH /api/messages/:id/rate` endpoint
|
||||
- [ ] Update `MessageBubble.tsx` with rating buttons
|
||||
- [ ] **COMMIT:** `feat(messages): add per-message model ratings`
|
||||
|
||||
**Effort:** ~2 hours
|
||||
|
||||
@ -367,10 +505,11 @@ LLM Lab has per-message thumbs up/down ratings with IndexedDB storage.
|
||||
|
||||
LLM Lab groups conversations into projects. LocalMemGPT has no grouping.
|
||||
|
||||
- Add `folders` table to SQLite
|
||||
- Add `folderId` column to conversations table
|
||||
- Add folder CRUD endpoints
|
||||
- Update Sidebar to show folder tree
|
||||
- [ ] Add `folders` table to SQLite
|
||||
- [ ] Add `folderId` column to conversations table
|
||||
- [ ] Add folder CRUD endpoints
|
||||
- [ ] Update Sidebar to show folder tree
|
||||
- [ ] **COMMIT:** `feat(conversations): add folder grouping`
|
||||
|
||||
**Effort:** ~4 hours
|
||||
|
||||
@ -378,10 +517,11 @@ LLM Lab groups conversations into projects. LocalMemGPT has no grouping.
|
||||
|
||||
LLM Lab has 6 built-in agents + custom agent editor. LocalMemGPT supports per-conversation system prompts but no reusable agent definitions.
|
||||
|
||||
- Add `agents` table to SQLite
|
||||
- Add agent CRUD endpoints
|
||||
- Add agent picker in new conversation flow
|
||||
- Port 6 built-in agent definitions
|
||||
- [ ] Add `agents` table to SQLite
|
||||
- [ ] Add agent CRUD endpoints
|
||||
- [ ] Add agent picker in new conversation flow
|
||||
- [ ] Port 6 built-in agent definitions
|
||||
- [ ] **COMMIT:** `feat(agents): add reusable agent definitions with built-in presets`
|
||||
|
||||
**Effort:** ~4 hours
|
||||
|
||||
@ -389,10 +529,11 @@ LLM Lab has 6 built-in agents + custom agent editor. LocalMemGPT supports per-co
|
||||
|
||||
LLM Lab has 30+ built-in quick actions (code review, explain, summarize, etc.).
|
||||
|
||||
- Add `quick_actions` table to SQLite
|
||||
- Add quick action CRUD + execute endpoints
|
||||
- Add Cmd+K command palette component
|
||||
- Port built-in quick action definitions
|
||||
- [ ] Add `quick_actions` table to SQLite
|
||||
- [ ] Add quick action CRUD + execute endpoints
|
||||
- [ ] Add Cmd+K command palette component
|
||||
- [ ] Port built-in quick action definitions
|
||||
- [ ] **COMMIT:** `feat(quick-actions): add command palette with 30+ built-in actions`
|
||||
|
||||
**Effort:** ~6 hours
|
||||
|
||||
@ -400,9 +541,10 @@ LLM Lab has 30+ built-in quick actions (code review, explain, summarize, etc.).
|
||||
|
||||
LLM Lab has whisper.cpp integration for speech-to-text.
|
||||
|
||||
- Add `POST /api/whisper/transcribe` endpoint (shell out to whisper binary)
|
||||
- Add microphone button in ChatView input bar
|
||||
- Detect whisper binary availability
|
||||
- [ ] Add `POST /api/whisper/transcribe` endpoint (shell out to whisper binary)
|
||||
- [ ] Add microphone button in ChatView input bar
|
||||
- [ ] Detect whisper binary availability
|
||||
- [ ] **COMMIT:** `feat(voice): add Whisper STT voice input`
|
||||
|
||||
**Effort:** ~4 hours
|
||||
|
||||
@ -410,9 +552,10 @@ LLM Lab has whisper.cpp integration for speech-to-text.
|
||||
|
||||
LLM Lab supports chain/race/vote multi-model orchestrations. LocalMemGPT has compare (parallel) but not chain/vote.
|
||||
|
||||
- Add `orchestrations` table
|
||||
- Add orchestration CRUD + execute endpoints
|
||||
- Add orchestration editor UI
|
||||
- [ ] Add `orchestrations` table
|
||||
- [ ] Add orchestration CRUD + execute endpoints
|
||||
- [ ] Add orchestration editor UI
|
||||
- [ ] **COMMIT:** `feat(orchestrations): add chain/race/vote multi-model pipelines`
|
||||
|
||||
**Effort:** ~1 day
|
||||
|
||||
@ -420,14 +563,257 @@ LLM Lab supports chain/race/vote multi-model orchestrations. LocalMemGPT has com
|
||||
|
||||
LLM Lab has cron-based scheduled prompts. Very niche feature.
|
||||
|
||||
- Add `scheduled_tasks` table
|
||||
- Add task scheduler (setInterval-based in backend)
|
||||
- Add task editor UI
|
||||
- [ ] Add `scheduled_tasks` table
|
||||
- [ ] Add task scheduler (setInterval-based in backend)
|
||||
- [ ] Add task editor UI
|
||||
- [ ] **COMMIT:** `feat(tasks): add cron-based scheduled prompts`
|
||||
|
||||
**Effort:** ~4 hours
|
||||
|
||||
---
|
||||
|
||||
### Phase 4 — DRY Refactoring & Common Platform Extractions
|
||||
|
||||
> **Independent of Phases 1–3** — can be executed before, after, or in parallel.
|
||||
>
|
||||
> **Effort:** ~3–5 days total
|
||||
> **Risk:** Low-Medium — new shared packages, consumers migrate incrementally
|
||||
> **Repo:** `learning_ai_common_plat` (new packages) + both local AI repos + ecosystem-wide
|
||||
|
||||
Code audit identified **7 extraction candidates** — duplicated patterns across the two local AI repos and the broader ecosystem.
|
||||
|
||||
#### 4.1 `@bytelyst/ollama-client` — Ollama API Client (NEW PACKAGE)
|
||||
|
||||
**Problem:** Both repos implement nearly identical Ollama API interaction code with duplicated NDJSON stream parsing.
|
||||
|
||||
| Repo | File | Functions | ~Lines |
|
||||
| ----------- | ---------------------------------------------- | ----------------------------------------------------------------------------- | ------ |
|
||||
| LocalMemGPT | `backend/src/lib/ollama.ts` | `listOllamaModels()`, `checkOllamaHealth()`, `streamChat()`, `getEmbedding()` | 117 |
|
||||
| LLM Lab | `dashboard/src/app/api/ollama/route.ts` | `fetchOllama()`, model list/pull/delete/show/load/unload | 128 |
|
||||
| LLM Lab | `dashboard/src/app/api/ollama/stream/route.ts` | raw streaming proxy | 37 |
|
||||
| LLM Lab | `dashboard/src/app/api/ollama/chat/route.ts` | chat proxy with llm-router | 109 |
|
||||
| LLM Lab | `dashboard/src/app/lib/ollama-config.ts` | `OLLAMA_URL` env resolution + WSL2 gateway detection | 40 |
|
||||
|
||||
**Shared NDJSON stream parsing pattern** (copy-pasted 5+ times across both repos):
|
||||
|
||||
```ts
|
||||
const reader = res.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() ?? '';
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
try {
|
||||
yield JSON.parse(line);
|
||||
} catch {
|
||||
/* skip */
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Proposed package:** `packages/ollama-client/`
|
||||
|
||||
```
|
||||
@bytelyst/ollama-client
|
||||
├── src/
|
||||
│ ├── config.ts # resolveOllamaUrl(env) — OLLAMA_URL/OLLAMA_HOST + WSL2 detection
|
||||
│ ├── client.ts # OllamaClient class: tags, ps, show, pull, load, unload, delete, version
|
||||
│ ├── stream.ts # streamChat(), streamGenerate() — AsyncGenerator<OllamaStreamChunk>
|
||||
│ ├── embed.ts # getEmbedding()
|
||||
│ ├── health.ts # checkHealth() with timeout
|
||||
│ ├── ndjson.ts # parseNdjsonStream() — reusable NDJSON async generator
|
||||
│ └── types.ts # OllamaModel, OllamaChatMessage, OllamaStreamChunk, etc.
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Consumers:**
|
||||
|
||||
- [ ] `learning_ai_local_memory_gpt/backend` — replace `lib/ollama.ts` (117 lines deleted)
|
||||
- [ ] `learning_ai_local_llms/dashboard` — replace `lib/ollama-config.ts` + 4 API route files simplified
|
||||
- [ ] `@bytelyst/llm-router` — could consume `@bytelyst/ollama-client` for its Ollama provider
|
||||
|
||||
**Effort:** ~1 day
|
||||
**Priority:** P0 — eliminates the largest duplication
|
||||
|
||||
- [ ] **COMMIT:** `feat(ollama-client): shared Ollama API client package`
|
||||
|
||||
---
|
||||
|
||||
#### 4.2 Extend `@bytelyst/fastify-sse` — Per-Request SSE Utilities [R4]
|
||||
|
||||
**Problem:** `@bytelyst/fastify-sse` provides `SSEHub` (broadcast to multiple clients) but NOT per-request SSE helpers. LocalMemGPT reinvented these as `sse-helpers.ts` (26 lines).
|
||||
|
||||
| Existing | What it does |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| `@bytelyst/fastify-sse` | `SSEHub` — multi-client broadcast pattern (hub/spoke) |
|
||||
| LocalMemGPT `sse-helpers.ts` | `startSSE()`, `sendSSEData()`, `sendSSEEvent()`, `endSSE()` — single-request streaming |
|
||||
|
||||
**Proposed:** Add per-request helpers to `@bytelyst/fastify-sse`:
|
||||
|
||||
```ts
|
||||
// New exports in @bytelyst/fastify-sse
|
||||
export { startSSE, sendSSEData, sendSSEEvent, endSSE } from './per-request.js';
|
||||
```
|
||||
|
||||
**Consumers:**
|
||||
|
||||
- [ ] `learning_ai_local_memory_gpt/backend` — replace `lib/sse-helpers.ts` with import from package
|
||||
- [ ] `learning_ai_trails/backend` — already uses `@bytelyst/fastify-sse` hub, could use per-request too
|
||||
|
||||
**Effort:** ~2 hours
|
||||
**Priority:** P1
|
||||
|
||||
- [ ] **COMMIT:** `feat(fastify-sse): add per-request SSE helpers`
|
||||
|
||||
---
|
||||
|
||||
#### 4.3 `@bytelyst/use-theme` — React Theme Toggle Hook (NEW PACKAGE)
|
||||
|
||||
**Problem:** Near-identical `useTheme()` hook duplicated across **6 web apps** — only the storage key and class-application strategy differ.
|
||||
|
||||
| Repo | File | Storage Key | Lines |
|
||||
| ----------- | ------------------------------------ | ----------- | ----- |
|
||||
| LocalMemGPT | `web/src/lib/use-theme.ts` | `lmg-theme` | 49 |
|
||||
| LLM Lab | `dashboard/src/app/lib/use-theme.ts` | `llm-theme` | 48 |
|
||||
| ChronoMind | `web/src/lib/use-theme.ts` | `cm-theme` | ~50 |
|
||||
| FlowMonk | `web/src/lib/use-theme.ts` | `fm-theme` | ~50 |
|
||||
| NomGap | `web/src/lib/use-theme.ts` | `ng-theme` | ~50 |
|
||||
| ActionTrail | `web/src/lib/use-theme.ts` [R5] | `at-theme` | ~50 |
|
||||
|
||||
**Proposed package:** `packages/use-theme/`
|
||||
|
||||
```ts
|
||||
export function useTheme(options?: { storageKey?: string; attribute?: 'class' | 'data-theme' }) {
|
||||
// Configurable storage key (default: `${PRODUCT_ID}-theme`)
|
||||
// Configurable DOM application (classList vs data-attribute)
|
||||
// Returns { theme, setTheme, toggleTheme }
|
||||
}
|
||||
```
|
||||
|
||||
**Effort:** ~2 hours
|
||||
**Priority:** P2 — saves ~300 total lines across 6 repos
|
||||
|
||||
- [ ] **COMMIT:** `feat(use-theme): shared React theme toggle hook`
|
||||
|
||||
---
|
||||
|
||||
#### 4.4 `@bytelyst/use-keyboard-shortcuts` — Keyboard Shortcuts Hook (NEW PACKAGE)
|
||||
|
||||
**Problem:** Near-identical `useKeyboardShortcuts()` hook duplicated across **5+ web apps**, each with slight variations in handler names.
|
||||
|
||||
| Repo | File |
|
||||
| ----------- | ------------------------------------------------------------ |
|
||||
| LocalMemGPT | `web/src/lib/use-keyboard-shortcuts.ts` (53 lines) |
|
||||
| LLM Lab | `dashboard/src/app/lib/use-keyboard-shortcuts.ts` (57 lines) |
|
||||
| ChronoMind | `web/src/lib/use-keyboard-shortcuts.ts` |
|
||||
| ActionTrail | `web/src/lib/use-keyboard-shortcuts.ts` |
|
||||
| NoteLett | `web/src/lib/use-keyboard-shortcuts.ts` |
|
||||
|
||||
**Proposed package:** `packages/use-keyboard-shortcuts/`
|
||||
|
||||
```ts
|
||||
export function useKeyboardShortcuts(shortcuts: ShortcutDef[]) {
|
||||
// Each ShortcutDef: { key, meta?, shift?, handler, allowInInput? }
|
||||
// Data-driven instead of hardcoded if/else chains
|
||||
}
|
||||
```
|
||||
|
||||
**Effort:** ~3 hours
|
||||
**Priority:** P2 — saves ~250 lines, improves consistency
|
||||
|
||||
- [ ] **COMMIT:** `feat(use-keyboard-shortcuts): shared React keyboard shortcuts hook`
|
||||
|
||||
---
|
||||
|
||||
#### 4.5 Ollama Model Types — Deduplicate into `@bytelyst/ollama-client` types
|
||||
|
||||
**Problem:** `OllamaModel` interface is defined independently in both repos with slightly different shapes.
|
||||
|
||||
| Repo | Definition |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| LocalMemGPT `ollama.ts` | `{ name, size, digest, modified_at, details?: { parameter_size, quantization_level, family } }` |
|
||||
| LLM Lab `types.ts` | `{ name, model, modified_at, size, digest, details: { families, family, format, parameter_size, quantization_level } }` |
|
||||
|
||||
**Resolution:** Canonical `OllamaModel` type in `@bytelyst/ollama-client/types.ts` — matches Ollama's actual API response. Both repos import from there.
|
||||
|
||||
**Effort:** included in 4.1
|
||||
**Priority:** P0 (part of 4.1)
|
||||
|
||||
---
|
||||
|
||||
#### 4.6 LLM Lab `format.ts` Utilities — Promote Reusable Functions
|
||||
|
||||
**Problem:** `format.ts` (96 lines) contains both LLM Lab-specific functions AND general-purpose utilities:
|
||||
|
||||
| Function | Scope | Extraction Target |
|
||||
| ------------------------- | ------------------------------------------------------ | --------------------------------------------------- |
|
||||
| `formatBytes()` | **Reusable** — any repo needs byte formatting | `@bytelyst/ollama-client` or new `@bytelyst/format` |
|
||||
| `estimateRam()` | LLM-infrastructure-specific | Keep in LLM Lab |
|
||||
| `checkMemoryFit()` | LLM-infrastructure-specific | Keep in LLM Lab |
|
||||
| `getModelBadges()` | LLM-infrastructure-specific | Keep in LLM Lab |
|
||||
| `estimateTokens()` | **Reusable** — any LLM app needs token estimation | `@bytelyst/ollama-client` |
|
||||
| `getModelContextWindow()` | **Reusable** — any LLM app needs context window lookup | `@bytelyst/ollama-client` |
|
||||
| `formatUptime()` | **Reusable** — monitoring/ops dashboards | `@bytelyst/ollama-client` |
|
||||
|
||||
**Effort:** ~1 hour (as part of 4.1)
|
||||
**Priority:** P1
|
||||
|
||||
---
|
||||
|
||||
#### 4.7 Client-Side SSE/NDJSON Stream Parser (Web)
|
||||
|
||||
**Problem:** The client-side SSE parsing pattern is copy-pasted 3 times in LocalMemGPT's `web/src/lib/api.ts` (`streamChat`, `streamCompare`, plus a `parseSSEBody` in tests). LLM Lab has 3 more copies in `ConversationView.tsx`, `compare/page.tsx`, and `mission-control/page.tsx`. **[R6] Note:** After Phase 1, `ConversationView.tsx` and `compare/page.tsx` are deleted — LLM Lab reduces to 1 copy (Mission Control). Total post-Phase-1 copies: 4 (3 LocalMemGPT + 1 LLM Lab).
|
||||
|
||||
**Proposed:** Export from `@bytelyst/ollama-client`:
|
||||
|
||||
```ts
|
||||
// Client-side SSE stream consumer
|
||||
export function consumeSSEStream(
|
||||
response: Response,
|
||||
onData: (data: Record<string, unknown>) => void,
|
||||
onDone: () => void
|
||||
): Promise<void>;
|
||||
|
||||
// Client-side NDJSON stream consumer
|
||||
export function consumeNdjsonStream<T>(
|
||||
response: Response,
|
||||
onChunk: (chunk: T) => void
|
||||
): Promise<void>;
|
||||
```
|
||||
|
||||
**Effort:** ~2 hours (as part of 4.1)
|
||||
**Priority:** P1 — eliminates 6 copy-pasted stream parsers
|
||||
|
||||
---
|
||||
|
||||
#### Phase 4 Summary
|
||||
|
||||
| # | Package | Action | Lines Saved | Repos Affected | Priority |
|
||||
| --- | ---------------------------------- | ---------- | ----------- | ----------------------- | -------- |
|
||||
| 4.1 | `@bytelyst/ollama-client` | **NEW** | ~350 | 2 local AI + llm-router | P0 |
|
||||
| 4.2 | `@bytelyst/fastify-sse` | **EXTEND** | ~26 | LocalMemGPT + future | P1 |
|
||||
| 4.3 | `@bytelyst/use-theme` | **NEW** | ~300 | 6 web apps | P2 |
|
||||
| 4.4 | `@bytelyst/use-keyboard-shortcuts` | **NEW** | ~250 | 5 web apps | P2 |
|
||||
| 4.5 | (types in 4.1) | — | included | 2 local AI | P0 |
|
||||
| 4.6 | (utils in 4.1) | — | ~40 | 2 local AI | P1 |
|
||||
| 4.7 | (client parsers in 4.1) | — | ~180 | 2 local AI | P1 |
|
||||
|
||||
**Total estimated savings:** ~1,150 lines of duplicated code across the ecosystem.
|
||||
|
||||
**Recommended execution order:**
|
||||
|
||||
1. **4.1** `@bytelyst/ollama-client` (biggest win, unblocks both repos)
|
||||
2. **4.2** Extend `@bytelyst/fastify-sse` (small, quick)
|
||||
3. **4.3 + 4.4** React hooks (lower priority, wider ecosystem impact — could batch with next DRY sweep)
|
||||
|
||||
---
|
||||
|
||||
## File Inventory Summary
|
||||
|
||||
### Phase 1 Deletions (Local LLM Lab)
|
||||
@ -511,240 +897,6 @@ LLM Lab has cron-based scheduled prompts. Very niche feature.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — DRY Refactoring & Common Platform Extractions
|
||||
|
||||
> **Note:** [R7] This phase was added as an addendum during the DRY audit (2026-03-29). It is independent of Phases 1-3 and can be executed before, after, or in parallel.
|
||||
>
|
||||
> **Effort:** ~3–5 days total
|
||||
> **Risk:** Low-Medium — new shared packages, consumers migrate incrementally
|
||||
> **Repo:** `learning_ai_common_plat` (new packages) + both local AI repos + ecosystem-wide
|
||||
|
||||
Code audit identified **7 extraction candidates** — duplicated patterns across the two local AI repos and the broader ecosystem.
|
||||
|
||||
### 4.1 `@bytelyst/ollama-client` — Ollama API Client (NEW PACKAGE)
|
||||
|
||||
**Problem:** Both repos implement nearly identical Ollama API interaction code with duplicated NDJSON stream parsing.
|
||||
|
||||
| Repo | File | Functions | ~Lines |
|
||||
| ----------- | ---------------------------------------------- | ----------------------------------------------------------------------------- | ------ |
|
||||
| LocalMemGPT | `backend/src/lib/ollama.ts` | `listOllamaModels()`, `checkOllamaHealth()`, `streamChat()`, `getEmbedding()` | 117 |
|
||||
| LLM Lab | `dashboard/src/app/api/ollama/route.ts` | `fetchOllama()`, model list/pull/delete/show/load/unload | 128 |
|
||||
| LLM Lab | `dashboard/src/app/api/ollama/stream/route.ts` | raw streaming proxy | 37 |
|
||||
| LLM Lab | `dashboard/src/app/api/ollama/chat/route.ts` | chat proxy with llm-router | 109 |
|
||||
| LLM Lab | `dashboard/src/app/lib/ollama-config.ts` | `OLLAMA_URL` env resolution + WSL2 gateway detection | 40 |
|
||||
|
||||
**Shared NDJSON stream parsing pattern** (copy-pasted 5+ times across both repos):
|
||||
|
||||
```ts
|
||||
const reader = res.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() ?? '';
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
try {
|
||||
yield JSON.parse(line);
|
||||
} catch {
|
||||
/* skip */
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Proposed package:** `packages/ollama-client/`
|
||||
|
||||
```
|
||||
@bytelyst/ollama-client
|
||||
├── src/
|
||||
│ ├── config.ts # resolveOllamaUrl(env) — OLLAMA_URL/OLLAMA_HOST + WSL2 detection
|
||||
│ ├── client.ts # OllamaClient class: tags, ps, show, pull, load, unload, delete, version
|
||||
│ ├── stream.ts # streamChat(), streamGenerate() — AsyncGenerator<OllamaStreamChunk>
|
||||
│ ├── embed.ts # getEmbedding()
|
||||
│ ├── health.ts # checkHealth() with timeout
|
||||
│ ├── ndjson.ts # parseNdjsonStream() — reusable NDJSON async generator
|
||||
│ └── types.ts # OllamaModel, OllamaChatMessage, OllamaStreamChunk, etc.
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Consumers:**
|
||||
|
||||
- `learning_ai_local_memory_gpt/backend` — replace `lib/ollama.ts` (117 lines deleted)
|
||||
- `learning_ai_local_llms/dashboard` — replace `lib/ollama-config.ts` + 4 API route files simplified
|
||||
- `@bytelyst/llm-router` — could consume `@bytelyst/ollama-client` for its Ollama provider
|
||||
|
||||
**Effort:** ~1 day
|
||||
**Priority:** P0 — eliminates the largest duplication
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Extend `@bytelyst/fastify-sse` — Per-Request SSE Utilities [R4]
|
||||
|
||||
**Problem:** `@bytelyst/fastify-sse` provides `SSEHub` (broadcast to multiple clients) but NOT per-request SSE helpers. LocalMemGPT reinvented these as `sse-helpers.ts` (26 lines).
|
||||
|
||||
| Existing | What it does |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| `@bytelyst/fastify-sse` | `SSEHub` — multi-client broadcast pattern (hub/spoke) |
|
||||
| LocalMemGPT `sse-helpers.ts` | `startSSE()`, `sendSSEData()`, `sendSSEEvent()`, `endSSE()` — single-request streaming |
|
||||
|
||||
**Proposed:** Add per-request helpers to `@bytelyst/fastify-sse`:
|
||||
|
||||
```ts
|
||||
// New exports in @bytelyst/fastify-sse
|
||||
export { startSSE, sendSSEData, sendSSEEvent, endSSE } from './per-request.js';
|
||||
```
|
||||
|
||||
**Consumers:**
|
||||
|
||||
- `learning_ai_local_memory_gpt/backend` — replace `lib/sse-helpers.ts` with import from package
|
||||
- `learning_ai_trails/backend` — already uses `@bytelyst/fastify-sse` hub, could use per-request too
|
||||
|
||||
**Effort:** ~2 hours
|
||||
**Priority:** P1
|
||||
|
||||
---
|
||||
|
||||
### 4.3 `@bytelyst/use-theme` — React Theme Toggle Hook (NEW PACKAGE)
|
||||
|
||||
**Problem:** Near-identical `useTheme()` hook duplicated across **6 web apps** — only the storage key and class-application strategy differ.
|
||||
|
||||
| Repo | File | Storage Key | Lines |
|
||||
| ----------- | ------------------------------------ | ----------- | ----- |
|
||||
| LocalMemGPT | `web/src/lib/use-theme.ts` | `lmg-theme` | 49 |
|
||||
| LLM Lab | `dashboard/src/app/lib/use-theme.ts` | `llm-theme` | 48 |
|
||||
| ChronoMind | `web/src/lib/use-theme.ts` | `cm-theme` | ~50 |
|
||||
| FlowMonk | `web/src/lib/use-theme.ts` | `fm-theme` | ~50 |
|
||||
| NomGap | `web/src/lib/use-theme.ts` | `ng-theme` | ~50 |
|
||||
| ActionTrail | `web/src/lib/use-theme.ts` [R5] | `at-theme` | ~50 |
|
||||
|
||||
**Proposed package:** `packages/use-theme/`
|
||||
|
||||
```ts
|
||||
export function useTheme(options?: { storageKey?: string; attribute?: 'class' | 'data-theme' }) {
|
||||
// Configurable storage key (default: `${PRODUCT_ID}-theme`)
|
||||
// Configurable DOM application (classList vs data-attribute)
|
||||
// Returns { theme, setTheme, toggleTheme }
|
||||
}
|
||||
```
|
||||
|
||||
**Effort:** ~2 hours
|
||||
**Priority:** P2 — saves ~300 total lines across 6 repos
|
||||
|
||||
---
|
||||
|
||||
### 4.4 `@bytelyst/use-keyboard-shortcuts` — Keyboard Shortcuts Hook (NEW PACKAGE)
|
||||
|
||||
**Problem:** Near-identical `useKeyboardShortcuts()` hook duplicated across **5+ web apps**, each with slight variations in handler names.
|
||||
|
||||
| Repo | File |
|
||||
| ----------- | ------------------------------------------------------------ |
|
||||
| LocalMemGPT | `web/src/lib/use-keyboard-shortcuts.ts` (53 lines) |
|
||||
| LLM Lab | `dashboard/src/app/lib/use-keyboard-shortcuts.ts` (57 lines) |
|
||||
| ChronoMind | `web/src/lib/use-keyboard-shortcuts.ts` |
|
||||
| ActionTrail | `web/src/lib/use-keyboard-shortcuts.ts` |
|
||||
| NoteLett | `web/src/lib/use-keyboard-shortcuts.ts` |
|
||||
|
||||
**Proposed package:** `packages/use-keyboard-shortcuts/`
|
||||
|
||||
```ts
|
||||
export function useKeyboardShortcuts(shortcuts: ShortcutDef[]) {
|
||||
// Each ShortcutDef: { key, meta?, shift?, handler, allowInInput? }
|
||||
// Data-driven instead of hardcoded if/else chains
|
||||
}
|
||||
```
|
||||
|
||||
**Effort:** ~3 hours
|
||||
**Priority:** P2 — saves ~250 lines, improves consistency
|
||||
|
||||
---
|
||||
|
||||
### 4.5 Ollama Model Types — Deduplicate into `@bytelyst/ollama-client` types
|
||||
|
||||
**Problem:** `OllamaModel` interface is defined independently in both repos with slightly different shapes.
|
||||
|
||||
| Repo | Definition |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| LocalMemGPT `ollama.ts` | `{ name, size, digest, modified_at, details?: { parameter_size, quantization_level, family } }` |
|
||||
| LLM Lab `types.ts` | `{ name, model, modified_at, size, digest, details: { families, family, format, parameter_size, quantization_level } }` |
|
||||
|
||||
**Resolution:** Canonical `OllamaModel` type in `@bytelyst/ollama-client/types.ts` — matches Ollama's actual API response. Both repos import from there.
|
||||
|
||||
**Effort:** included in 4.1
|
||||
**Priority:** P0 (part of 4.1)
|
||||
|
||||
---
|
||||
|
||||
### 4.6 LLM Lab `format.ts` Utilities — Promote Reusable Functions
|
||||
|
||||
**Problem:** `format.ts` (96 lines) contains both LLM Lab-specific functions AND general-purpose utilities:
|
||||
|
||||
| Function | Scope | Extraction Target |
|
||||
| ------------------------- | ------------------------------------------------------ | --------------------------------------------------- |
|
||||
| `formatBytes()` | **Reusable** — any repo needs byte formatting | `@bytelyst/ollama-client` or new `@bytelyst/format` |
|
||||
| `estimateRam()` | LLM-infrastructure-specific | Keep in LLM Lab |
|
||||
| `checkMemoryFit()` | LLM-infrastructure-specific | Keep in LLM Lab |
|
||||
| `getModelBadges()` | LLM-infrastructure-specific | Keep in LLM Lab |
|
||||
| `estimateTokens()` | **Reusable** — any LLM app needs token estimation | `@bytelyst/ollama-client` |
|
||||
| `getModelContextWindow()` | **Reusable** — any LLM app needs context window lookup | `@bytelyst/ollama-client` |
|
||||
| `formatUptime()` | **Reusable** — monitoring/ops dashboards | `@bytelyst/ollama-client` |
|
||||
|
||||
**Effort:** ~1 hour (as part of 4.1)
|
||||
**Priority:** P1
|
||||
|
||||
---
|
||||
|
||||
### 4.7 Client-Side SSE/NDJSON Stream Parser (Web)
|
||||
|
||||
**Problem:** The client-side SSE parsing pattern is copy-pasted 3 times in LocalMemGPT's `web/src/lib/api.ts` (`streamChat`, `streamCompare`, plus a `parseSSEBody` in tests). LLM Lab has 3 more copies in `ConversationView.tsx`, `compare/page.tsx`, and `mission-control/page.tsx`. **[R6] Note:** After Phase 1, `ConversationView.tsx` and `compare/page.tsx` are deleted — LLM Lab reduces to 1 copy (Mission Control). Total post-Phase-1 copies: 4 (3 LocalMemGPT + 1 LLM Lab).
|
||||
|
||||
**Proposed:** Export from `@bytelyst/ollama-client`:
|
||||
|
||||
```ts
|
||||
// Client-side SSE stream consumer
|
||||
export function consumeSSEStream(
|
||||
response: Response,
|
||||
onData: (data: Record<string, unknown>) => void,
|
||||
onDone: () => void
|
||||
): Promise<void>;
|
||||
|
||||
// Client-side NDJSON stream consumer
|
||||
export function consumeNdjsonStream<T>(
|
||||
response: Response,
|
||||
onChunk: (chunk: T) => void
|
||||
): Promise<void>;
|
||||
```
|
||||
|
||||
**Effort:** ~2 hours (as part of 4.1)
|
||||
**Priority:** P1 — eliminates 6 copy-pasted stream parsers
|
||||
|
||||
---
|
||||
|
||||
### Phase 4 Summary
|
||||
|
||||
| # | Package | Action | Lines Saved | Repos Affected | Priority |
|
||||
| --- | ---------------------------------- | ---------- | ----------- | ----------------------- | -------- |
|
||||
| 4.1 | `@bytelyst/ollama-client` | **NEW** | ~350 | 2 local AI + llm-router | P0 |
|
||||
| 4.2 | `@bytelyst/fastify-sse` | **EXTEND** | ~26 | LocalMemGPT + future | P1 |
|
||||
| 4.3 | `@bytelyst/use-theme` | **NEW** | ~300 | 6 web apps | P2 |
|
||||
| 4.4 | `@bytelyst/use-keyboard-shortcuts` | **NEW** | ~250 | 5 web apps | P2 |
|
||||
| 4.5 | (types in 4.1) | — | included | 2 local AI | P0 |
|
||||
| 4.6 | (utils in 4.1) | — | ~40 | 2 local AI | P1 |
|
||||
| 4.7 | (client parsers in 4.1) | — | ~180 | 2 local AI | P1 |
|
||||
|
||||
**Total estimated savings:** ~1,150 lines of duplicated code across the ecosystem.
|
||||
|
||||
**Recommended execution order:**
|
||||
|
||||
1. **4.1** `@bytelyst/ollama-client` (biggest win, unblocks both repos)
|
||||
2. **4.2** Extend `@bytelyst/fastify-sse` (small, quick)
|
||||
3. **4.3 + 4.4** React hooks (lower priority, wider ecosystem impact — could batch with next DRY sweep)
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. ~~**TTS/Whisper in Mission Control:**~~ **[F13] RESOLVED.** Mission Control uses `api/whisper` (status), `api/whisper/transcribe` (test), `api/tts` (engine status), `api/ollama/chat` (model testing), `api/ollama/stream` (benchmarks/compare), `api/ollama/logs` (log viewer). All 6 routes are kept.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user