diff --git a/docs/TECH_STACK_INDEPENDENCE.md b/docs/TECH_STACK_INDEPENDENCE.md index c6b8652c8..108251509 100644 --- a/docs/TECH_STACK_INDEPENDENCE.md +++ b/docs/TECH_STACK_INDEPENDENCE.md @@ -94,18 +94,13 @@ Brevo ist SPOF für alle Transaktions-Emails (Verifizierung, Passwort-Reset). #### 2.1 Alle LLM-Requests über mana-llm routen -**Aufwand:** 1 Woche | **Impact:** Hoch +**Status: ✅ ERLEDIGT** (2026-03-24) -Aktuell nutzen einige Apps direkt Cloud-SDKs (OpenAI, Gemini, Azure). Wenn alles über `mana-llm` läuft: -- Ein Switch reicht um Provider zu wechseln -- Zentrales Logging, Rate-Limiting, Cost-Tracking -- Einfacherer Wechsel zu lokalen Modellen - -**Betroffene Apps:** -- Context App → `apps/context/apps/backend/src/ai/ai.service.ts` -- NutriPhi → Google Gemini SDK direkt -- Planta → Google Gemini SDK direkt -- Matrix Project Doc Bot → OpenAI SDK direkt +Alle 9 Backends nutzen jetzt `@manacore/shared-llm` → `mana-llm` Gateway: +- Auth, Chat, Context, NutriPhi, Planta, Traces, ManaDeck, Bot Services, Matrix Bots +- Google Gemini als automatischer Fallback wenn Ollama überlastet +- OpenAI SDK komplett entfernt (Project Doc Bot) +- Google Gemini SDK entfernt (ManaDeck) #### 2.2 PostgreSQL Backup stärken @@ -201,9 +196,9 @@ NutriPhi und Planta nutzen Google Gemini Vision. Alternativen via Ollama: | Prio | Maßnahme | Aufwand | Gewinn | |------|----------|---------|--------| -| **1** | Picture App → mana-image-gen | 1-2 Tage | Replicate-Kosten = 0, volle Kontrolle | -| **2** | Project Doc Bot → Ollama + mana-stt | 1 Tag | OpenAI-Abhängigkeit weg | -| **3** | Alle LLM-Calls über mana-llm routen | 1 Woche | Zentrale Provider-Kontrolle | +| ~~**1**~~ | ~~Picture App → mana-image-gen~~ | ✅ Erledigt | Lokales FLUX.2 klein als Default, Replicate für Premium | +| ~~**2**~~ | ~~Project Doc Bot → Ollama + mana-stt~~ | ✅ Erledigt | OpenAI SDK entfernt, nutzt mana-llm + mana-stt | +| ~~**3**~~ | ~~Alle LLM-Calls über mana-llm routen~~ | ✅ Erledigt | @manacore/shared-llm + Google Fallback | | **4** | PostgreSQL Backup mit pgBackRest | 1-2 Tage | Disaster Recovery | | **5** | Brevo → Postal/Stalwart | 2-3 Tage | Email-Unabhängigkeit | | **6** | Landing Pages self-hosted | 0.5 Tage | Cloudflare Pages weg | @@ -216,8 +211,15 @@ NutriPhi und Planta nutzen Google Gemini Vision. Alternativen via Ollama: ## Zusammenfassung -**Aktuell self-hosted:** ~75% der Infrastruktur -**Nach Roadmap (Prio 1-6):** ~90% +**Aktuell self-hosted:** ~80% der Infrastruktur (Stand: 2026-03-24) +**Nach Roadmap (Prio 4-6):** ~90% **Unvermeidbare Cloud-Abhängigkeiten:** Stripe (Payment Gateway), Google OAuth (Contacts API) -Der Stack ist bereits sehr gut aufgestellt. Die größten Quick Wins sind die Integration von `mana-image-gen` in die Picture App und das Routing aller LLM-Calls über `mana-llm`. Die kritischste Verbesserung ist die Backup-Strategie und Server-Redundanz. +**Erledigte Meilensteine (2026-03-24):** +- ✅ Alle LLM-Calls über `mana-llm` geroutet (9 Backends, `@manacore/shared-llm`) +- ✅ Google Gemini Fallback in mana-llm (automatisch bei Ollama-Überlastung) +- ✅ Picture App nutzt lokales `mana-image-gen` (FLUX.2 klein) als Default +- ✅ Project Doc Bot: OpenAI SDK komplett entfernt +- ✅ ManaDeck: Google Gemini SDK entfernt + +**Nächste Schritte:** PostgreSQL Backup (Prio 4), E-Mail-Unabhängigkeit (Prio 5), Landing Pages self-hosted (Prio 6). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35da066a3..84f630362 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8564,9 +8564,6 @@ importers: matrix-bot-sdk: specifier: ^0.7.1 version: 0.7.1 - openai: - specifier: ^4.77.0 - version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76) postgres: specifier: ^3.4.5 version: 3.4.7 @@ -8579,7 +8576,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9(esbuild@0.27.4) + version: 10.4.9(esbuild@0.19.12) '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -31227,7 +31224,6 @@ snapshots: - supports-color - typescript - utf-8-validate - optional: true '@expo/code-signing-certificates@0.0.5': dependencies: @@ -31473,7 +31469,6 @@ snapshots: optionalDependencies: react: 19.2.4 react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - optional: true '@expo/dom-webview@55.0.3(expo@52.0.47)(react-native@0.76.7(@babel/core@7.28.5)(@babel/preset-env@7.28.5(@babel/core@7.28.5))(@types/react@18.3.27)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': dependencies: @@ -31542,7 +31537,6 @@ snapshots: expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - optional: true '@expo/env@0.4.2': dependencies: @@ -31724,7 +31718,6 @@ snapshots: react: 19.2.4 react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) stacktrace-parser: 0.1.11 - optional: true '@expo/mcp-tunnel@0.0.8': dependencies: @@ -31939,7 +31932,7 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) transitivePeerDependencies: - bufferutil - supports-color @@ -32246,7 +32239,7 @@ snapshots: '@expo/json-file': 10.0.12 '@react-native/normalize-colors': 0.83.2 debug: 4.4.3 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) resolve-from: 5.0.0 semver: 7.7.3 xml2js: 0.6.0 @@ -32320,7 +32313,6 @@ snapshots: react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: - supports-color - optional: true '@expo/rudder-sdk-node@1.1.1(encoding@0.1.13)': dependencies: @@ -32443,7 +32435,6 @@ snapshots: expo-font: 55.0.4(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) react: 19.2.4 react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - optional: true '@expo/ws-tunnel@1.0.6': {} @@ -33780,6 +33771,32 @@ snapshots: - uglify-js - webpack-cli + '@nestjs/cli@10.4.9(esbuild@0.19.12)': + dependencies: + '@angular-devkit/core': 17.3.11(chokidar@3.6.0) + '@angular-devkit/schematics': 17.3.11(chokidar@3.6.0) + '@angular-devkit/schematics-cli': 17.3.11(chokidar@3.6.0) + '@nestjs/schematics': 10.2.3(chokidar@3.6.0)(typescript@5.7.2) + chalk: 4.1.2 + chokidar: 3.6.0 + cli-table3: 0.6.5 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.19.12)) + glob: 10.4.5 + inquirer: 8.2.6 + node-emoji: 1.11.0 + ora: 5.4.1 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.2.0 + typescript: 5.7.2 + webpack: 5.97.1(esbuild@0.19.12) + webpack-node-externals: 3.0.0 + transitivePeerDependencies: + - esbuild + - uglify-js + - webpack-cli + '@nestjs/cli@10.4.9(esbuild@0.27.4)': dependencies: '@angular-devkit/core': 17.3.11(chokidar@3.6.0) @@ -36066,8 +36083,7 @@ snapshots: '@react-native/assets-registry@0.83.2': {} - '@react-native/assets-registry@0.84.1': - optional: true + '@react-native/assets-registry@0.84.1': {} '@react-native/babel-plugin-codegen@0.76.7(@babel/preset-env@7.28.5(@babel/core@7.28.5))': dependencies: @@ -36368,7 +36384,6 @@ snapshots: nullthrows: 1.1.1 tinyglobby: 0.2.15 yargs: 17.7.2 - optional: true '@react-native/community-cli-plugin@0.76.7(@babel/core@7.28.5)(@babel/preset-env@7.28.5(@babel/core@7.28.5))(encoding@0.1.13)': dependencies: @@ -36467,7 +36482,6 @@ snapshots: - bufferutil - supports-color - utf-8-validate - optional: true '@react-native/debugger-frontend@0.76.7': {} @@ -36479,8 +36493,7 @@ snapshots: '@react-native/debugger-frontend@0.83.2': {} - '@react-native/debugger-frontend@0.84.1': - optional: true + '@react-native/debugger-frontend@0.84.1': {} '@react-native/debugger-shell@0.83.2': dependencies: @@ -36494,7 +36507,6 @@ snapshots: fb-dotslash: 0.5.8 transitivePeerDependencies: - supports-color - optional: true '@react-native/dev-middleware@0.76.7': dependencies: @@ -36607,7 +36619,6 @@ snapshots: - bufferutil - supports-color - utf-8-validate - optional: true '@react-native/gradle-plugin@0.76.7': {} @@ -36619,8 +36630,7 @@ snapshots: '@react-native/gradle-plugin@0.83.2': {} - '@react-native/gradle-plugin@0.84.1': - optional: true + '@react-native/gradle-plugin@0.84.1': {} '@react-native/js-polyfills@0.76.7': {} @@ -36632,8 +36642,7 @@ snapshots: '@react-native/js-polyfills@0.83.2': {} - '@react-native/js-polyfills@0.84.1': - optional: true + '@react-native/js-polyfills@0.84.1': {} '@react-native/metro-babel-transformer@0.76.7(@babel/core@7.28.5)(@babel/preset-env@7.28.5(@babel/core@7.28.5))': dependencies: @@ -36669,8 +36678,7 @@ snapshots: '@react-native/normalize-colors@0.83.2': {} - '@react-native/normalize-colors@0.84.1': - optional: true + '@react-native/normalize-colors@0.84.1': {} '@react-native/virtualized-lists@0.76.7(@types/react@18.3.27)(react-native@0.76.7(@babel/core@7.28.5)(@babel/preset-env@7.28.5(@babel/core@7.28.5))(@types/react@18.3.27)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': dependencies: @@ -36779,7 +36787,6 @@ snapshots: react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) optionalDependencies: '@types/react': 19.2.14 - optional: true '@react-navigation/bottom-tabs@7.15.5(@react-navigation/native@7.1.33(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: @@ -42292,7 +42299,7 @@ snapshots: resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.28.4 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) transitivePeerDependencies: - '@babel/core' - supports-color @@ -45397,7 +45404,6 @@ snapshots: transitivePeerDependencies: - supports-color - typescript - optional: true expo-audio@55.0.8(expo-asset@55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3))(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0): dependencies: @@ -45567,7 +45573,6 @@ snapshots: transitivePeerDependencies: - supports-color - typescript - optional: true expo-dev-client@5.0.20(expo@52.0.47): dependencies: @@ -45755,7 +45760,6 @@ snapshots: dependencies: expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - optional: true expo-font@13.0.4(expo@52.0.47)(react@18.3.1): dependencies: @@ -45861,7 +45865,6 @@ snapshots: fontfaceobserver: 2.3.0 react: 19.2.4 react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - optional: true expo-glass-effect@55.0.8(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0): dependencies: @@ -46001,7 +46004,6 @@ snapshots: dependencies: expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@6.1.2)(expo-router@55.0.5)(react-dom@19.2.4(react@19.2.4))(react-native-webview@13.12.2(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4))(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 - optional: true expo-linear-gradient@15.0.7(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): dependencies: @@ -46284,7 +46286,6 @@ snapshots: invariant: 2.2.4 react: 19.2.4 react-native: 0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4) - optional: true expo-notifications@55.0.12(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3): dependencies: @@ -47410,7 +47411,6 @@ snapshots: - supports-color - typescript - utf-8-validate - optional: true exponential-backoff@3.1.3: {} @@ -47812,6 +47812,23 @@ snapshots: forever-agent@0.6.1: {} + fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.19.12)): + dependencies: + '@babel/code-frame': 7.27.1 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 8.3.6(typescript@5.7.2) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.7.3 + tapable: 2.3.0 + typescript: 5.7.2 + webpack: 5.97.1(esbuild@0.19.12) + fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.27.4)): dependencies: '@babel/code-frame': 7.27.1 @@ -48573,8 +48590,7 @@ snapshots: hermes-compiler@0.14.1: {} - hermes-compiler@250829098.0.9: - optional: true + hermes-compiler@250829098.0.9: {} hermes-estree@0.23.1: {} @@ -55540,7 +55556,6 @@ snapshots: - bufferutil - supports-color - utf-8-validate - optional: true react-refresh@0.14.2: {} @@ -57249,6 +57264,17 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 + terser-webpack-plugin@5.3.14(esbuild@0.19.12)(webpack@5.97.1(esbuild@0.19.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.1 + webpack: 5.97.1(esbuild@0.19.12) + optionalDependencies: + esbuild: 0.19.12 + terser-webpack-plugin@5.3.14(esbuild@0.27.4)(webpack@5.100.2(esbuild@0.27.4)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -59317,6 +59343,36 @@ snapshots: - esbuild - uglify-js + webpack@5.97.1(esbuild@0.19.12): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + browserslist: 4.28.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(esbuild@0.19.12)(webpack@5.97.1(esbuild@0.19.12)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.97.1(esbuild@0.27.4): dependencies: '@types/eslint-scope': 3.7.7 diff --git a/services/matrix-project-doc-bot/CLAUDE.md b/services/matrix-project-doc-bot/CLAUDE.md index 50e63375f..1bbf4401b 100644 --- a/services/matrix-project-doc-bot/CLAUDE.md +++ b/services/matrix-project-doc-bot/CLAUDE.md @@ -10,7 +10,7 @@ Matrix Project Doc Bot collects photos, voice notes, and text for projects and g - **Matrix**: matrix-bot-sdk - **Database**: Drizzle ORM + PostgreSQL - **Storage**: S3 (MinIO) -- **AI**: OpenAI (Whisper for transcription, GPT-4o-mini for generation) +- **AI**: mana-llm (Ollama/Gemma 3 for generation), mana-stt (Whisper for transcription) — fully self-hosted ## Commands diff --git a/services/matrix-project-doc-bot/package.json b/services/matrix-project-doc-bot/package.json index 85516fb24..3a44dc27f 100644 --- a/services/matrix-project-doc-bot/package.json +++ b/services/matrix-project-doc-bot/package.json @@ -27,14 +27,15 @@ "db:studio": "drizzle-kit studio" }, "dependencies": { + "@aws-sdk/client-s3": "^3.721.0", + "@aws-sdk/s3-request-presigner": "^3.721.0", "@manacore/bot-services": "workspace:*", "@manacore/matrix-bot-common": "workspace:*", + "@manacore/shared-llm": "workspace:^", "@nestjs/common": "^10.4.15", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.4.15", "@nestjs/platform-express": "^10.4.15", - "@aws-sdk/client-s3": "^3.721.0", - "@aws-sdk/s3-request-presigner": "^3.721.0", "drizzle-orm": "^0.38.3", "matrix-bot-sdk": "^0.7.1", "openai": "^4.77.0", diff --git a/services/matrix-project-doc-bot/src/app.module.ts b/services/matrix-project-doc-bot/src/app.module.ts index dd25e43aa..f2b97564b 100644 --- a/services/matrix-project-doc-bot/src/app.module.ts +++ b/services/matrix-project-doc-bot/src/app.module.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { LlmModule } from '@manacore/shared-llm'; import { HealthController, createHealthProvider } from '@manacore/matrix-bot-common'; import { DatabaseModule } from './database/database.module'; import { BotModule } from './bot/bot.module'; @@ -11,6 +12,14 @@ import configuration from './config/configuration'; isGlobal: true, load: [configuration], }), + LlmModule.forRootAsync({ + imports: [ConfigModule], + useFactory: (config: ConfigService) => ({ + manaLlmUrl: config.get('MANA_LLM_URL') || 'http://localhost:3025', + debug: config.get('NODE_ENV') === 'development', + }), + inject: [ConfigService], + }), DatabaseModule, BotModule, ], diff --git a/services/matrix-project-doc-bot/src/generation/generation.service.ts b/services/matrix-project-doc-bot/src/generation/generation.service.ts index 7724fa3ca..535d8d0c4 100644 --- a/services/matrix-project-doc-bot/src/generation/generation.service.ts +++ b/services/matrix-project-doc-bot/src/generation/generation.service.ts @@ -1,6 +1,6 @@ import { Injectable, Inject, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; import { eq, desc } from 'drizzle-orm'; +import { LlmClientService } from '@manacore/shared-llm'; import { DATABASE_CONNECTION } from '../database/database.module'; import { generations, projectItems, projects } from '../database/schema'; import { BLOG_STYLES } from '../config/configuration'; @@ -12,16 +12,11 @@ type Database = PostgresJsDatabase; @Injectable() export class GenerationService { private readonly logger = new Logger(GenerationService.name); - private readonly manaLlmUrl: string; - private readonly model: string; constructor( @Inject(DATABASE_CONNECTION) private db: Database, - private configService: ConfigService - ) { - this.manaLlmUrl = this.configService.get('MANA_LLM_URL') || 'http://localhost:3025'; - this.model = this.configService.get('openai.model') || 'ollama/gemma3:4b'; - } + private readonly llm: LlmClientService + ) {} async generateBlogpost(projectId: string, style: keyof typeof BLOG_STYLES): Promise { // Get project info @@ -70,39 +65,21 @@ Erstellt am: ${project.createdAt.toLocaleDateString('de-DE')} Die folgenden Inhalte wurden während des Projekts gesammelt:`; - const response = await fetch(`${this.manaLlmUrl}/v1/chat/completions`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - model: this.model, - messages: [ - { role: 'system', content: systemPrompt }, - { role: 'user', content: contentSummary }, - ], - temperature: 0.7, - max_tokens: 2000, - }), - signal: AbortSignal.timeout(120000), + const result = await this.llm.chat(contentSummary, { + systemPrompt, + temperature: 0.7, + maxTokens: 2000, }); - if (!response.ok) { - const errorText = await response.text(); - this.logger.error(`mana-llm error: ${response.status} - ${errorText}`); - throw new Error(`LLM generation failed: ${response.status}`); - } - - const data = await response.json(); - const content = data.choices?.[0]?.message?.content || ''; - // Save generation await this.db.insert(generations).values({ projectId, style, - content, + content: result.content, }); this.logger.log(`Generated ${style} blogpost for project ${projectId}`); - return content; + return result.content; } async getLatestGeneration(projectId: string) {