mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:01:08 +02:00
Mirror of github.com/Memo-2023/mana-monorepo
First real-world consumer of the @mana/shared-llm tier framework.
After STT transcription completes for a voice memo, the memos store
fire-and-forgets a generateTitleTask into the persistent task queue
with refType:'memo' + refId:memoId. A module-side watcher subscribed
via Dexie liveQuery to completed task rows writes the result back
into memo.title and deletes the queue row to mark it consumed.
What this commit ships:
apps/mana/apps/web/src/lib/llm-tasks/generate-title.ts
- generateTitleTask: minTier='none', contentClass='personal'
- runLlm: sends a German system prompt asking for a 3-7 word
title, defensive cleanup of any quotes/markdown the model
might leak through despite the prompt
- runRules: takes the first sentence (split on .!?\n), caps
at maxWords/60-chars, returns a non-empty fallback string.
Predictable and free, works on every device including the
ones where the user has opted out of all LLM tiers.
apps/mana/apps/web/src/lib/llm-task-registry.ts
- Register generateTitleTask alongside extractDate + summarize
so the queue processor can resolve the name back to the
task object after a row is pulled from the persistent table.
apps/mana/apps/web/src/lib/modules/memoro/stores/memos.svelte.ts
- After transcribeMemo successfully writes the transcript +
processingStatus:'completed', enqueue a generateTitleTask
tagged with refType:'memo' + refId + priority:1. Skips the
enqueue if the memo already has a non-empty title (so
manually-titled memos aren't overwritten on re-transcription)
or if the transcript came back empty.
- Wrapped in try/catch — queue failures must NEVER break the
transcription happy path.
apps/mana/apps/web/src/lib/modules/memoro/llm-watcher.svelte.ts
- startMemoroLlmWatcher() / stopMemoroLlmWatcher()
- Subscribes via Dexie liveQuery to llmQueueDb.tasks rows
where state='done', taskName='common.generateTitle',
refType='memo'. For each row:
1. Skip + delete row if result isn't a string (defensive)
2. Skip + delete row if memo no longer exists (deleted
between enqueue and result)
3. Skip + delete row if memo already has a manual title
(user typed one during the LLM round-trip)
4. Otherwise: encryptRecord + memoTable.update with
{ title: result, updatedAt: now }, then delete the
queue row to mark it consumed.
- Module-scope subscription handle, idempotent start/stop.
apps/mana/apps/web/src/routes/(app)/+layout.svelte
- startMemoroLlmWatcher() in handleAuthReady's Phase A right
after startLlmQueue(). The watcher needs to run regardless
of whether the user is currently on /memoro — a memo
transcribing in the background should auto-title even
while the user is doing something else.
- stopMemoroLlmWatcher() in onDestroy alongside stopLlmQueue().
End-to-end flow with a Tier 0 user (no AI enabled):
1. User records a memo via voice capture
2. memos.svelte.ts createWithTranscription() inserts the memo
with processingStatus:'processing'
3. transcribeMemo POSTs the audio to mana-stt, awaits the
transcript
4. Successful transcript → memos.svelte.ts writes
{ transcript, processingStatus:'completed' } to memoTable
5. Same function enqueues generateTitleTask with the transcript
6. LlmTaskQueue processor picks it up (the queue is running in
the background since layout init), calls
orchestrator.run(generateTitleTask, { text: transcript })
7. Orchestrator: Tier 0 user → no LLM tier → falls through to
runRules() which returns the first-sentence heuristic
8. Queue marks the row done with the rules-tier title string
9. Memoro watcher's liveQuery fires with the new completed row
10. Watcher writes title + deletes the queue row
11. ListView's existing useLiveQuery on memoTable picks up the
title change automatically
End-to-end flow with a Browser-tier user:
Steps 1-6 identical, then:
7. Orchestrator: browser tier ready → calls
generateTitleTask.runLlm with the BrowserBackend
8. Web Worker (Phase 3) runs Gemma 4 E2B against a 32-token
budget, returns a 3-7 word German title
9-11. Same as Tier 0 — the title lands in memo.title without
the user clicking anything
This is the validation the entire 4-phase architecture was built
for: a module-side auto-feature that's completely tier-agnostic,
fire-and-forget, persistent across reloads, and that gracefully
degrades from Gemma 4 down to a regex when the user has opted out.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .changeset | ||
| .claude | ||
| .github | ||
| .husky | ||
| apps | ||
| docker | ||
| docs | ||
| games | ||
| load-tests | ||
| NewAppIdeas/Roblox Reimagined | ||
| packages | ||
| patches | ||
| scripts | ||
| services | ||
| tests | ||
| .dockerignore | ||
| .editorconfig | ||
| .env.development | ||
| .env.macmini.example | ||
| .env.secrets.example | ||
| .gitignore | ||
| .npmrc | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc.json | ||
| CLAUDE.md | ||
| cloudflared-config.yml | ||
| docker-compose.dev.yml | ||
| docker-compose.macmini.yml | ||
| docker-compose.test.yml | ||
| eslint.config.mjs | ||
| gift-codes-2026-02-14.txt | ||
| lint-staged.config.js | ||
| package.json | ||
| playwright.config.ts | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| TROUBLESHOOTING.md | ||
| turbo.json | ||
| vitest.config.ts | ||
Mana Monorepo
Monorepo containing all Mana projects — a self-hosted multi-app ecosystem with shared packages and unified tooling.
Projects
| Project | Description | Apps |
|---|---|---|
| mana | Multi-app ecosystem platform | Expo mobile, SvelteKit web |
| chat | AI chat application | NestJS backend, Expo mobile, SvelteKit web, Astro landing |
| todo | Task management | NestJS backend, SvelteKit web, Astro landing |
| calendar | Calendar & scheduling | NestJS backend, SvelteKit web, Astro landing |
| clock | Pomodoro & time tracking | NestJS backend, SvelteKit web, Astro landing |
| contacts | Contact management | NestJS backend, SvelteKit web |
| picture | AI image generation | NestJS backend, Expo mobile, SvelteKit web, Astro landing |
| cards | Card/deck management | NestJS backend, Expo mobile, SvelteKit web |
| zitare | Daily inspiration quotes | NestJS backend, Expo mobile, SvelteKit web, Astro landing |
| mukke | Music player | NestJS backend, SvelteKit web |
| planta | Plant care tracker | NestJS backend, SvelteKit web |
| storage | Cloud storage | NestJS backend, SvelteKit web |
| questions | Q&A with web search | SvelteKit web |
| skilltree | Skill tree visualization | NestJS backend, SvelteKit web |
| nutriphi | Nutrition tracking | NestJS backend, SvelteKit web |
| citycorners | City guide | NestJS backend, SvelteKit web, Astro landing |
| presi | Presentation tool | NestJS backend, SvelteKit web |
| photos | Photo management | NestJS backend, SvelteKit web |
Getting Started
Prerequisites
- Node.js 20+
- pnpm 9.15.0+
- Docker (for PostgreSQL, Redis, MinIO)
Installation
pnpm install
Development
# Start infrastructure (PostgreSQL, Redis, MinIO)
pnpm docker:up
# Start any app with auto DB setup
pnpm dev:chat:full
pnpm dev:todo:full
pnpm dev:calendar:full
pnpm dev:contacts:full
# Build & quality
pnpm run build
pnpm run type-check
pnpm run format
See CLAUDE.md for comprehensive development documentation.
Architecture
mana-monorepo/
├── apps/ # Product applications
├── services/ # Microservices (auth, search, LLM, bots)
├── packages/ # Shared packages
├── docker/ # Docker configuration
└── scripts/ # Development & deployment scripts
Tooling
- Package Manager: pnpm 9.15.0
- Build System: Turborepo
- Formatting: Prettier (tabs, single quotes, 100 char width)
- Hosting: Mac Mini (self-hosted) via Docker + Cloudflare Tunnel
- Analytics: Umami (stats.mana.how)
License
Private - All rights reserved