libre-webui 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +204 -0
  3. package/backend/dist/db.d.ts +19 -0
  4. package/backend/dist/db.d.ts.map +1 -0
  5. package/backend/dist/db.js +355 -0
  6. package/backend/dist/db.js.map +1 -0
  7. package/backend/dist/env.d.ts +2 -0
  8. package/backend/dist/env.d.ts.map +1 -0
  9. package/backend/dist/env.js +22 -0
  10. package/backend/dist/env.js.map +1 -0
  11. package/backend/dist/index.d.ts +4 -0
  12. package/backend/dist/index.d.ts.map +1 -0
  13. package/backend/dist/index.js +751 -0
  14. package/backend/dist/index.js.map +1 -0
  15. package/backend/dist/middleware/auth.d.ts +18 -0
  16. package/backend/dist/middleware/auth.d.ts.map +1 -0
  17. package/backend/dist/middleware/auth.js +98 -0
  18. package/backend/dist/middleware/auth.js.map +1 -0
  19. package/backend/dist/middleware/index.d.ts +5 -0
  20. package/backend/dist/middleware/index.d.ts.map +1 -0
  21. package/backend/dist/middleware/index.js +62 -0
  22. package/backend/dist/middleware/index.js.map +1 -0
  23. package/backend/dist/models/personaModel.d.ts +37 -0
  24. package/backend/dist/models/personaModel.d.ts.map +1 -0
  25. package/backend/dist/models/personaModel.js +269 -0
  26. package/backend/dist/models/personaModel.js.map +1 -0
  27. package/backend/dist/models/userModel.d.ts +86 -0
  28. package/backend/dist/models/userModel.d.ts.map +1 -0
  29. package/backend/dist/models/userModel.js +212 -0
  30. package/backend/dist/models/userModel.js.map +1 -0
  31. package/backend/dist/routes/auth.d.ts +3 -0
  32. package/backend/dist/routes/auth.d.ts.map +1 -0
  33. package/backend/dist/routes/auth.js +389 -0
  34. package/backend/dist/routes/auth.js.map +1 -0
  35. package/backend/dist/routes/chat.d.ts +3 -0
  36. package/backend/dist/routes/chat.d.ts.map +1 -0
  37. package/backend/dist/routes/chat.js +767 -0
  38. package/backend/dist/routes/chat.js.map +1 -0
  39. package/backend/dist/routes/documents.d.ts +3 -0
  40. package/backend/dist/routes/documents.d.ts.map +1 -0
  41. package/backend/dist/routes/documents.js +244 -0
  42. package/backend/dist/routes/documents.js.map +1 -0
  43. package/backend/dist/routes/ollama.d.ts +3 -0
  44. package/backend/dist/routes/ollama.d.ts.map +1 -0
  45. package/backend/dist/routes/ollama.js +549 -0
  46. package/backend/dist/routes/ollama.js.map +1 -0
  47. package/backend/dist/routes/personas.d.ts +3 -0
  48. package/backend/dist/routes/personas.d.ts.map +1 -0
  49. package/backend/dist/routes/personas.js +505 -0
  50. package/backend/dist/routes/personas.js.map +1 -0
  51. package/backend/dist/routes/plugins.d.ts +3 -0
  52. package/backend/dist/routes/plugins.d.ts.map +1 -0
  53. package/backend/dist/routes/plugins.js +417 -0
  54. package/backend/dist/routes/plugins.js.map +1 -0
  55. package/backend/dist/routes/preferences.d.ts +3 -0
  56. package/backend/dist/routes/preferences.d.ts.map +1 -0
  57. package/backend/dist/routes/preferences.js +303 -0
  58. package/backend/dist/routes/preferences.js.map +1 -0
  59. package/backend/dist/routes/tts.d.ts +3 -0
  60. package/backend/dist/routes/tts.d.ts.map +1 -0
  61. package/backend/dist/routes/tts.js +304 -0
  62. package/backend/dist/routes/tts.js.map +1 -0
  63. package/backend/dist/routes/users.d.ts +3 -0
  64. package/backend/dist/routes/users.d.ts.map +1 -0
  65. package/backend/dist/routes/users.js +246 -0
  66. package/backend/dist/routes/users.js.map +1 -0
  67. package/backend/dist/services/authService.d.ts +51 -0
  68. package/backend/dist/services/authService.d.ts.map +1 -0
  69. package/backend/dist/services/authService.js +153 -0
  70. package/backend/dist/services/authService.js.map +1 -0
  71. package/backend/dist/services/chatService.d.ts +52 -0
  72. package/backend/dist/services/chatService.d.ts.map +1 -0
  73. package/backend/dist/services/chatService.js +645 -0
  74. package/backend/dist/services/chatService.js.map +1 -0
  75. package/backend/dist/services/documentService.d.ts +34 -0
  76. package/backend/dist/services/documentService.d.ts.map +1 -0
  77. package/backend/dist/services/documentService.js +428 -0
  78. package/backend/dist/services/documentService.js.map +1 -0
  79. package/backend/dist/services/encryptionService.d.ts +62 -0
  80. package/backend/dist/services/encryptionService.d.ts.map +1 -0
  81. package/backend/dist/services/encryptionService.js +284 -0
  82. package/backend/dist/services/encryptionService.js.map +1 -0
  83. package/backend/dist/services/memoryService.d.ts +140 -0
  84. package/backend/dist/services/memoryService.d.ts.map +1 -0
  85. package/backend/dist/services/memoryService.js +867 -0
  86. package/backend/dist/services/memoryService.js.map +1 -0
  87. package/backend/dist/services/mutationEngineService.d.ts +49 -0
  88. package/backend/dist/services/mutationEngineService.d.ts.map +1 -0
  89. package/backend/dist/services/mutationEngineService.js +432 -0
  90. package/backend/dist/services/mutationEngineService.js.map +1 -0
  91. package/backend/dist/services/ollamaService.d.ts +55 -0
  92. package/backend/dist/services/ollamaService.d.ts.map +1 -0
  93. package/backend/dist/services/ollamaService.js +450 -0
  94. package/backend/dist/services/ollamaService.js.map +1 -0
  95. package/backend/dist/services/personaService.d.ts +67 -0
  96. package/backend/dist/services/personaService.d.ts.map +1 -0
  97. package/backend/dist/services/personaService.js +373 -0
  98. package/backend/dist/services/personaService.js.map +1 -0
  99. package/backend/dist/services/pluginService.d.ts +42 -0
  100. package/backend/dist/services/pluginService.d.ts.map +1 -0
  101. package/backend/dist/services/pluginService.js +961 -0
  102. package/backend/dist/services/pluginService.js.map +1 -0
  103. package/backend/dist/services/preferencesService.d.ts +35 -0
  104. package/backend/dist/services/preferencesService.d.ts.map +1 -0
  105. package/backend/dist/services/preferencesService.js +255 -0
  106. package/backend/dist/services/preferencesService.js.map +1 -0
  107. package/backend/dist/services/simpleGitHubOAuth.d.ts +48 -0
  108. package/backend/dist/services/simpleGitHubOAuth.d.ts.map +1 -0
  109. package/backend/dist/services/simpleGitHubOAuth.js +203 -0
  110. package/backend/dist/services/simpleGitHubOAuth.js.map +1 -0
  111. package/backend/dist/services/simpleHuggingFaceOAuth.d.ts +43 -0
  112. package/backend/dist/services/simpleHuggingFaceOAuth.d.ts.map +1 -0
  113. package/backend/dist/services/simpleHuggingFaceOAuth.js +159 -0
  114. package/backend/dist/services/simpleHuggingFaceOAuth.js.map +1 -0
  115. package/backend/dist/services/userService.d.ts +1 -0
  116. package/backend/dist/services/userService.d.ts.map +1 -0
  117. package/backend/dist/services/userService.js +18 -0
  118. package/backend/dist/services/userService.js.map +1 -0
  119. package/backend/dist/storage.d.ts +55 -0
  120. package/backend/dist/storage.d.ts.map +1 -0
  121. package/backend/dist/storage.js +741 -0
  122. package/backend/dist/storage.js.map +1 -0
  123. package/backend/dist/test-encryption.d.ts +2 -0
  124. package/backend/dist/test-encryption.d.ts.map +1 -0
  125. package/backend/dist/test-encryption.js +64 -0
  126. package/backend/dist/test-encryption.js.map +1 -0
  127. package/backend/dist/types/index.d.ts +523 -0
  128. package/backend/dist/types/index.d.ts.map +1 -0
  129. package/backend/dist/types/index.js +31 -0
  130. package/backend/dist/types/index.js.map +1 -0
  131. package/backend/dist/utils/generationUtils.d.ts +10 -0
  132. package/backend/dist/utils/generationUtils.d.ts.map +1 -0
  133. package/backend/dist/utils/generationUtils.js +49 -0
  134. package/backend/dist/utils/generationUtils.js.map +1 -0
  135. package/backend/dist/utils/hash.d.ts +29 -0
  136. package/backend/dist/utils/hash.d.ts.map +1 -0
  137. package/backend/dist/utils/hash.js +73 -0
  138. package/backend/dist/utils/hash.js.map +1 -0
  139. package/backend/dist/utils/jwt.d.ts +37 -0
  140. package/backend/dist/utils/jwt.d.ts.map +1 -0
  141. package/backend/dist/utils/jwt.js +86 -0
  142. package/backend/dist/utils/jwt.js.map +1 -0
  143. package/bin/cli.js +150 -0
  144. package/electron/main.js +322 -0
  145. package/frontend/dist/_redirects +1 -0
  146. package/frontend/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  147. package/frontend/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  148. package/frontend/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  149. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  150. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  151. package/frontend/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  152. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  153. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  154. package/frontend/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  155. package/frontend/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  156. package/frontend/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  157. package/frontend/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  158. package/frontend/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  159. package/frontend/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  160. package/frontend/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  161. package/frontend/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  162. package/frontend/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  163. package/frontend/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  164. package/frontend/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  165. package/frontend/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  166. package/frontend/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  167. package/frontend/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  168. package/frontend/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  169. package/frontend/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  170. package/frontend/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  171. package/frontend/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  172. package/frontend/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  173. package/frontend/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  174. package/frontend/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  175. package/frontend/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  176. package/frontend/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  177. package/frontend/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  178. package/frontend/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  179. package/frontend/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  180. package/frontend/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  181. package/frontend/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  182. package/frontend/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  183. package/frontend/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  184. package/frontend/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  185. package/frontend/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  186. package/frontend/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  187. package/frontend/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  188. package/frontend/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  189. package/frontend/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  190. package/frontend/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  191. package/frontend/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  192. package/frontend/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  193. package/frontend/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  194. package/frontend/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  195. package/frontend/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  196. package/frontend/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  197. package/frontend/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  198. package/frontend/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  199. package/frontend/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  200. package/frontend/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  201. package/frontend/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  202. package/frontend/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  203. package/frontend/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  204. package/frontend/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  205. package/frontend/dist/assets/index-CRQkB7Wz.js +3 -0
  206. package/frontend/dist/css/index-B1OjddR-.css +1 -0
  207. package/frontend/dist/favicon-dark.png +0 -0
  208. package/frontend/dist/favicon-light.png +0 -0
  209. package/frontend/dist/index.html +23 -0
  210. package/frontend/dist/js/ArtifactContainer-c_oi7XMs.js +23 -0
  211. package/frontend/dist/js/ArtifactDemoPage-CdfwJVXu.js +98 -0
  212. package/frontend/dist/js/ChatPage-CyotkmS0.js +281 -0
  213. package/frontend/dist/js/ModelsPage-DNaziPHc.js +2 -0
  214. package/frontend/dist/js/PersonasPage-DcnbJf8Q.js +13 -0
  215. package/frontend/dist/js/UserManagementPage-DtTf92dS.js +1 -0
  216. package/frontend/dist/js/markdown-vendor-D-79K2xZ.js +22 -0
  217. package/frontend/dist/js/react-vendor-N--QU9DW.js +8 -0
  218. package/frontend/dist/js/router-vendor-B-t91v39.js +3 -0
  219. package/frontend/dist/js/ui-vendor-VxSCY_bv.js +177 -0
  220. package/frontend/dist/js/utils-vendor-DNzxLBGx.js +6 -0
  221. package/frontend/dist/logo-dark.png +0 -0
  222. package/frontend/dist/logo-light.png +0 -0
  223. package/frontend/dist/logo.svg +14 -0
  224. package/package.json +128 -0
  225. package/plugins/anthropic.json +25 -0
  226. package/plugins/elevenlabs.json +58 -0
  227. package/plugins/gemini.json +57 -0
  228. package/plugins/github.json +23 -0
  229. package/plugins/groq.json +25 -0
  230. package/plugins/mistral.json +73 -0
  231. package/plugins/openai-tts.json +38 -0
  232. package/plugins/openai.json +132 -0
  233. package/plugins/openrouter.json +353 -0
@@ -0,0 +1,645 @@
1
+ /*
2
+ * Libre WebUI
3
+ * Copyright (C) 2025 Kroonen AI, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at:
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { v4 as uuidv4 } from 'uuid';
18
+ import storageService from '../storage.js';
19
+ import preferencesService from './preferencesService.js';
20
+ import { personaService } from './personaService.js';
21
+ import { memoryService } from './memoryService.js';
22
+ import { mutationEngineService } from './mutationEngineService.js';
23
+ class ChatService {
24
+ constructor() {
25
+ this.sessions = new Map();
26
+ this.loadSessions();
27
+ }
28
+ loadSessions() {
29
+ try {
30
+ const sessionsArray = storageService.getAllSessions();
31
+ this.sessions = new Map(sessionsArray.map(session => [session.id, session]));
32
+ console.log(`Loaded ${sessionsArray.length} sessions from storage`);
33
+ }
34
+ catch (error) {
35
+ console.error('Failed to load sessions:', error);
36
+ }
37
+ }
38
+ saveSessions() {
39
+ // This method is kept for compatibility but individual session saving is now handled by storage service
40
+ // The storage service handles both SQLite and JSON fallback
41
+ }
42
+ async createSession(model, title, userId = 'default', personaId) {
43
+ const sessionId = uuidv4();
44
+ const now = Date.now();
45
+ console.log(`🚀 ChatService.createSession: sessionId=%s, userId=%s, model=%s, personaId=%s`, sessionId, userId, model, personaId);
46
+ const session = {
47
+ id: sessionId,
48
+ title: title || 'New Chat',
49
+ messages: [],
50
+ model,
51
+ createdAt: now,
52
+ updatedAt: now,
53
+ personaId,
54
+ };
55
+ // Add system message - prioritize persona system prompt over global preferences
56
+ let systemMessage = '';
57
+ let systemMessageSource = 'none';
58
+ console.log(`[DEBUG] createSession model: "${model}", starts with persona: ${model.startsWith('persona:')}`);
59
+ // If model is a persona, try to get the persona's system prompt
60
+ if (model.startsWith('persona:')) {
61
+ try {
62
+ const personaIdFromModel = model.replace('persona:', '');
63
+ console.log(`[DEBUG] Extracting persona ID: "%s" for user: "%s"`, personaIdFromModel, userId);
64
+ const { personaService } = await import('./personaService.js');
65
+ // Get persona for the current user only (no fallback to maintain privacy)
66
+ const persona = await personaService.getPersonaById(personaIdFromModel, userId);
67
+ console.log(`[DEBUG] Persona lookup for user ${userId}:`, persona ? `Found: ${persona.name}` : 'Not found');
68
+ if (persona && persona.parameters?.system_prompt) {
69
+ systemMessage = persona.parameters.system_prompt.trim();
70
+ systemMessageSource = `persona:${persona.name}`;
71
+ console.log(`[DEBUG] Using persona system prompt from ${persona.name}: "${systemMessage.substring(0, 100)}..."`);
72
+ }
73
+ else {
74
+ console.log(`[DEBUG] No system prompt found in persona or persona not found`);
75
+ }
76
+ }
77
+ catch (error) {
78
+ console.error(`❌ Error getting persona system prompt:`, error);
79
+ }
80
+ }
81
+ // If no persona system prompt, fall back to global preferences
82
+ if (!systemMessage) {
83
+ const globalSystemMessage = preferencesService.getSystemMessage(userId);
84
+ if (globalSystemMessage && globalSystemMessage.trim()) {
85
+ systemMessage = globalSystemMessage.trim();
86
+ systemMessageSource = 'preferences';
87
+ console.log(`[DEBUG] Using global system message: "${systemMessage.substring(0, 100)}..."`);
88
+ }
89
+ }
90
+ // Add the system message if we have one
91
+ if (systemMessage) {
92
+ console.log(`✅ Adding system message to session: ${sessionId} (source: ${systemMessageSource})`);
93
+ const systemMsg = {
94
+ id: uuidv4(),
95
+ role: 'system',
96
+ content: systemMessage,
97
+ timestamp: now,
98
+ };
99
+ session.messages.push(systemMsg);
100
+ session.updatedAt = now;
101
+ }
102
+ this.sessions.set(sessionId, session);
103
+ console.log(`📝 Session stored in cache: ${sessionId}`);
104
+ // Save to storage with user ID
105
+ storageService.saveSession(session, userId);
106
+ console.log(`💾 Session saved to storage: ${sessionId} for user ${userId}`);
107
+ return session;
108
+ }
109
+ getSession(sessionId, userId = 'default') {
110
+ console.log(`🔍 ChatService.getSession: sessionId=${sessionId}, userId=${userId}`);
111
+ // First try to get from memory cache
112
+ let session = this.sessions.get(sessionId);
113
+ console.log(`📝 Session in cache: ${session ? 'YES' : 'NO'}`);
114
+ // If not in cache, try to load from storage (with user verification)
115
+ if (!session) {
116
+ session = storageService.getSession(sessionId, userId);
117
+ console.log(`💾 Session in storage: ${session ? 'YES' : 'NO'}`);
118
+ if (session) {
119
+ this.sessions.set(sessionId, session);
120
+ }
121
+ }
122
+ else {
123
+ // If found in cache, we should still verify it belongs to this user
124
+ // by checking the storage service (which has the user verification logic)
125
+ const verifiedSession = storageService.getSession(sessionId, userId);
126
+ console.log(`✅ Session verification: ${verifiedSession ? 'PASSED' : 'FAILED'}`);
127
+ if (!verifiedSession) {
128
+ // Session doesn't belong to this user, remove from cache and return undefined
129
+ console.log(`❌ Removing session ${sessionId} from cache - verification failed`);
130
+ this.sessions.delete(sessionId);
131
+ return undefined;
132
+ }
133
+ }
134
+ console.log(`🎯 Returning session: ${session ? session.id : 'undefined'}`);
135
+ return session;
136
+ }
137
+ getAllSessions(userId = 'default') {
138
+ // Load fresh data from storage to ensure we have the latest
139
+ const sessionsArray = storageService.getAllSessions(userId);
140
+ // Update memory cache with user-specific sessions
141
+ // Note: We don't clear the entire cache since other users might be using it
142
+ sessionsArray.forEach(session => {
143
+ this.sessions.set(session.id, session);
144
+ });
145
+ return sessionsArray;
146
+ }
147
+ async updateSession(sessionId, updates, userId = 'default') {
148
+ // First verify the session belongs to the user
149
+ const session = this.getSession(sessionId, userId);
150
+ if (!session)
151
+ return undefined;
152
+ const updatedSession = {
153
+ ...session,
154
+ ...updates,
155
+ updatedAt: Date.now(),
156
+ };
157
+ // If the model is being updated and it's a persona, update the system message and personaId
158
+ if (updates.model && updates.model !== session.model) {
159
+ console.log(`[DEBUG] updateSession: Model changed from "${session.model}" to "${updates.model}"`);
160
+ if (updates.model.startsWith('persona:')) {
161
+ const personaId = updates.model.replace('persona:', '');
162
+ console.log(`[DEBUG] updateSession: Extracting persona ID: %s`, personaId);
163
+ // Update personaId
164
+ updatedSession.personaId = personaId;
165
+ // Update system message with persona's system prompt
166
+ await this.updateSystemMessageForPersona(updatedSession, personaId, userId);
167
+ }
168
+ else {
169
+ // If switching away from a persona to a regular model, clear personaId and use default system message
170
+ updatedSession.personaId = undefined;
171
+ this.updateSystemMessageToDefault(updatedSession, userId);
172
+ }
173
+ }
174
+ this.sessions.set(sessionId, updatedSession);
175
+ storageService.saveSession(updatedSession, userId);
176
+ return updatedSession;
177
+ }
178
+ addMessage(sessionId, message, userId = 'default') {
179
+ console.log(`[addMessage] Called with sessionId=${sessionId}, role=${message.role}, id=${message.id}, userId=${userId}`);
180
+ // First verify the session belongs to the user
181
+ const session = this.getSession(sessionId, userId);
182
+ if (!session) {
183
+ console.log(`[addMessage] Session not found: ${sessionId}`);
184
+ return undefined;
185
+ }
186
+ console.log(`[addMessage] Session found with ${session.messages.length} messages`);
187
+ const messageId = message.id || uuidv4();
188
+ // Check if message with this ID already exists to prevent duplicates
189
+ const existingMessage = session.messages.find(msg => msg.id === messageId);
190
+ if (existingMessage) {
191
+ console.log(`[addMessage] Message with ID already exists, skipping duplicate: ${messageId}, existing content length: ${existingMessage.content.length}`);
192
+ return existingMessage;
193
+ }
194
+ const newMessage = {
195
+ ...message,
196
+ id: messageId,
197
+ timestamp: Date.now(),
198
+ };
199
+ // If this is a branch message (has parentId), update sibling messages
200
+ if (newMessage.parentId) {
201
+ const parentId = newMessage.parentId;
202
+ // Mark all sibling messages (including the parent) as inactive
203
+ for (const msg of session.messages) {
204
+ const isSibling = msg.id === parentId || msg.parentId === parentId;
205
+ if (isSibling) {
206
+ msg.isActive = false;
207
+ // Ensure the parent has branchIndex 0 if it doesn't have one
208
+ if (msg.branchIndex === undefined) {
209
+ msg.branchIndex = 0;
210
+ }
211
+ // Update siblingCount for all siblings
212
+ msg.siblingCount = (newMessage.branchIndex || 0) + 1;
213
+ }
214
+ }
215
+ }
216
+ session.messages.push(newMessage);
217
+ session.updatedAt = Date.now();
218
+ // Process advanced persona features if applicable
219
+ console.log(`[DEBUG] addMessage: Checking advanced processing - personaId: ${session.personaId}, messageRole: ${message.role}, content length: ${message.content.length}`);
220
+ if (session.personaId) {
221
+ console.log(`[DEBUG] addMessage: PersonaId exists, processing message role: ${message.role}`);
222
+ if (message.role === 'user') {
223
+ console.log(`[DEBUG] addMessage: Starting advanced user interaction processing for persona ${session.personaId}`);
224
+ this.processAdvancedPersonaInteraction(session.personaId, userId, message.content, session).catch((error) => console.error('Advanced persona processing error:', error));
225
+ }
226
+ else if (message.role === 'assistant') {
227
+ console.log(`[DEBUG] addMessage: Starting advanced assistant response processing for persona ${session.personaId}`);
228
+ this.processAdvancedPersonaResponse(session.personaId, userId, message.content).catch((error) => console.error('Advanced persona response processing error:', error));
229
+ }
230
+ }
231
+ else {
232
+ console.log(`[DEBUG] addMessage: No personaId found, skipping advanced processing`);
233
+ }
234
+ // Auto-generate title from first user message
235
+ const userMessages = session.messages.filter(msg => msg.role === 'user');
236
+ if (userMessages.length === 1 &&
237
+ message.role === 'user' &&
238
+ session.title === 'New Chat') {
239
+ session.title = this.generateTitle(message.content);
240
+ }
241
+ this.sessions.set(sessionId, session);
242
+ storageService.saveSession(session, userId);
243
+ console.log(`[addMessage] Message saved successfully: ${newMessage.id}, session now has ${session.messages.length} messages`);
244
+ return newMessage;
245
+ }
246
+ updateMessage(sessionId, messageId, updates, userId = 'default') {
247
+ // First verify the session belongs to the user
248
+ const session = this.getSession(sessionId, userId);
249
+ if (!session) {
250
+ console.error('Session not found or access denied:', sessionId, userId);
251
+ return undefined;
252
+ }
253
+ // Find the message to update
254
+ const messageIndex = session.messages.findIndex(msg => msg.id === messageId);
255
+ if (messageIndex === -1) {
256
+ console.error('Message not found:', messageId);
257
+ return undefined;
258
+ }
259
+ // Update the message
260
+ const updatedMessage = {
261
+ ...session.messages[messageIndex],
262
+ ...updates,
263
+ timestamp: Date.now(), // Always update timestamp
264
+ };
265
+ session.messages[messageIndex] = updatedMessage;
266
+ session.updatedAt = Date.now();
267
+ // Save updated session
268
+ this.sessions.set(sessionId, session);
269
+ storageService.saveSession(session, userId);
270
+ console.log(`✅ Updated message ${messageId} in session ${sessionId}`);
271
+ return updatedMessage;
272
+ }
273
+ deleteSession(sessionId, userId = 'default') {
274
+ // First verify the session belongs to the user
275
+ const session = this.getSession(sessionId, userId);
276
+ if (!session)
277
+ return false;
278
+ const deleted = storageService.deleteSession(sessionId, userId);
279
+ if (deleted) {
280
+ this.sessions.delete(sessionId);
281
+ }
282
+ return deleted;
283
+ }
284
+ clearAllSessions(userId = 'default') {
285
+ // Get all sessions for the user first
286
+ const userSessions = this.getAllSessions(userId);
287
+ // Remove them from memory cache
288
+ userSessions.forEach(session => {
289
+ this.sessions.delete(session.id);
290
+ });
291
+ // Clear them from storage
292
+ userSessions.forEach(session => {
293
+ storageService.deleteSession(session.id, userId);
294
+ });
295
+ }
296
+ generateTitle(content) {
297
+ // Generate a concise title from the first message
298
+ const words = content.trim().split(/\s+/).slice(0, 6);
299
+ let title = words.join(' ');
300
+ if (title.length > 50) {
301
+ title = title.substring(0, 47) + '...';
302
+ }
303
+ return title || 'New Chat';
304
+ }
305
+ getMessagesForContext(sessionId, maxMessages = 10) {
306
+ const session = this.sessions.get(sessionId);
307
+ if (!session)
308
+ return [];
309
+ // Separate system messages from conversation messages
310
+ // Only include active messages (isActive !== false) to respect branch selection
311
+ const systemMessages = session.messages.filter(msg => msg.role === 'system');
312
+ const conversationMessages = session.messages.filter(msg => msg.role !== 'system' && msg.isActive !== false);
313
+ // Take the last N conversation messages, but always include all system messages first
314
+ const recentConversation = conversationMessages.slice(-maxMessages);
315
+ // Return system messages first, then conversation messages
316
+ const contextMessages = [...systemMessages, ...recentConversation];
317
+ // Debug: Log the system messages being sent
318
+ if (systemMessages.length > 0) {
319
+ console.log(`🎯 [DEBUG] Context for session ${sessionId}:`);
320
+ systemMessages.forEach((msg, index) => {
321
+ console.log(` System message ${index + 1}: "${msg.content.substring(0, 100)}${msg.content.length > 100 ? '...' : ''}"`);
322
+ });
323
+ }
324
+ return contextMessages;
325
+ }
326
+ async updateSystemMessageForPersona(session, personaId, userId) {
327
+ try {
328
+ console.log(`[DEBUG] updateSystemMessageForPersona: Starting for persona %s, user %s`, personaId, userId);
329
+ // Get persona for the current user only (no fallback to maintain privacy)
330
+ const persona = await personaService.getPersonaById(personaId, userId);
331
+ if (persona && persona.parameters?.system_prompt) {
332
+ const newSystemMessage = persona.parameters.system_prompt.trim();
333
+ console.log(`[DEBUG] updateSystemMessageForPersona: Found persona system prompt: "${newSystemMessage.substring(0, 100)}..."`);
334
+ // Update or replace the system message
335
+ this.replaceSystemMessage(session, newSystemMessage);
336
+ console.log(`[DEBUG] updateSystemMessageForPersona: Successfully updated system message for session ${session.id}`);
337
+ }
338
+ else {
339
+ console.log(`[DEBUG] updateSystemMessageForPersona: No system prompt found for persona %s, using default`, personaId);
340
+ // Fallback to default system message
341
+ this.updateSystemMessageToDefault(session, userId);
342
+ }
343
+ }
344
+ catch (error) {
345
+ console.error(`[ERROR] updateSystemMessageForPersona: Error getting persona %s:`, personaId, error);
346
+ // Fallback to default system message
347
+ this.updateSystemMessageToDefault(session, userId);
348
+ }
349
+ }
350
+ updateSystemMessageToDefault(session, userId) {
351
+ const defaultSystemMessage = preferencesService.getSystemMessage(userId);
352
+ console.log(`[DEBUG] updateSystemMessageToDefault: Using default system message: "${defaultSystemMessage.substring(0, 100)}..."`);
353
+ this.replaceSystemMessage(session, defaultSystemMessage);
354
+ }
355
+ replaceSystemMessage(session, newSystemMessage) {
356
+ // Find existing system message
357
+ const systemMessageIndex = session.messages.findIndex(msg => msg.role === 'system');
358
+ if (systemMessageIndex !== -1) {
359
+ // Update existing system message
360
+ session.messages[systemMessageIndex] = {
361
+ ...session.messages[systemMessageIndex],
362
+ content: newSystemMessage,
363
+ timestamp: Date.now(),
364
+ };
365
+ console.log(`[DEBUG] replaceSystemMessage: Updated existing system message`);
366
+ }
367
+ else {
368
+ // Add new system message at the beginning
369
+ const systemMessage = {
370
+ id: uuidv4(),
371
+ role: 'system',
372
+ content: newSystemMessage,
373
+ timestamp: Date.now(),
374
+ };
375
+ session.messages.unshift(systemMessage);
376
+ console.log(`[DEBUG] replaceSystemMessage: Added new system message`);
377
+ }
378
+ }
379
+ /**
380
+ * Process advanced persona interactions: memory storage, retrieval, and mutations
381
+ */
382
+ async processAdvancedPersonaInteraction(personaId, userId, userMessage, session) {
383
+ try {
384
+ console.log(`[ADVANCED-DEBUG] processAdvancedPersonaInteraction called - personaId: ${personaId}, userId: ${userId}`);
385
+ // Check if persona has advanced features enabled for current user only (no fallback to maintain privacy)
386
+ const persona = await personaService.getPersonaById(personaId, userId);
387
+ console.log(`[ADVANCED-DEBUG] Persona lookup for user ${userId}:`, persona ? `Found: ${persona.name}` : 'Not found');
388
+ if (!persona) {
389
+ console.log(`[ADVANCED-DEBUG] No persona found for user, exiting`);
390
+ return;
391
+ }
392
+ // Check if this persona has advanced features (memory or adaptive learning)
393
+ const hasAdvancedFeatures = persona.embedding_model || persona.memory_settings;
394
+ console.log(`[ADVANCED-DEBUG] Advanced features check:`, {
395
+ embedding_model: persona.embedding_model,
396
+ memory_settings: persona.memory_settings,
397
+ hasAdvancedFeatures,
398
+ });
399
+ if (!hasAdvancedFeatures) {
400
+ console.log(`[ADVANCED-DEBUG] No advanced features, exiting`);
401
+ return;
402
+ }
403
+ console.log(`[ADVANCED] Processing interaction for persona ${persona.name} (${personaId})`);
404
+ // Get advanced settings
405
+ const embeddingModel = persona.embedding_model || 'nomic-embed-text';
406
+ console.log(`[ADVANCED] Using embedding model: ${embeddingModel}`);
407
+ console.log(`[ADVANCED] Advanced features enabled`);
408
+ // 1. Store the user message as a memory
409
+ console.log(`[ADVANCED] Storing user message as memory...`);
410
+ await memoryService.storeMemory(userId, personaId, userMessage, embeddingModel, undefined, // context
411
+ 0.7 // importance score
412
+ );
413
+ console.log(`[ADVANCED] ✅ User message stored successfully`);
414
+ // 2. Search for relevant memories
415
+ console.log(`[ADVANCED] Searching for relevant memories...`);
416
+ const relevantMemories = await memoryService.searchMemories(userId, personaId, userMessage, embeddingModel, 5, // topK
417
+ 0.3 // similarity threshold
418
+ );
419
+ console.log(`[ADVANCED] Found ${relevantMemories.length} relevant memories`);
420
+ if (relevantMemories.length > 0) {
421
+ console.log(`[ADVANCED] Memory details:`, relevantMemories.map(m => ({
422
+ content: m.entry.content.substring(0, 100) + '...',
423
+ similarity: (m.similarity_score * 100).toFixed(1) + '%',
424
+ })));
425
+ }
426
+ // 3. Process potential mutations based on the interaction
427
+ if (relevantMemories.length > 0) {
428
+ await mutationEngineService.processMutation(userMessage, persona, // Cast to Persona for mutation engine
429
+ userId, relevantMemories);
430
+ }
431
+ // 4. Update system message with relevant memories if any found
432
+ if (relevantMemories.length > 0) {
433
+ await this.updateSystemMessageWithMemories(session, persona, relevantMemories, userId);
434
+ }
435
+ }
436
+ catch (error) {
437
+ console.error(`[ADVANCED] Error processing persona interaction:`, error);
438
+ }
439
+ }
440
+ /**
441
+ * Update system message to include relevant memories
442
+ */
443
+ async updateSystemMessageWithMemories(session, persona, memories, userId) {
444
+ try {
445
+ const baseSystemPrompt = persona.parameters?.system_prompt || '';
446
+ // Get core memories (always-include important facts, preferences, instructions)
447
+ const coreMemories = await memoryService.getCoreMemories(userId, persona.id, 3);
448
+ // Combine core memories with contextual memories, avoiding duplicates
449
+ const coreIds = new Set(coreMemories.map(m => m.id));
450
+ const contextualMemories = memories
451
+ .filter(m => !coreIds.has(m.entry.id))
452
+ .slice(0, 3);
453
+ if (coreMemories.length === 0 && contextualMemories.length === 0)
454
+ return;
455
+ // Build memory context sections
456
+ let memoryContext = '';
457
+ // Core memories section (always relevant)
458
+ if (coreMemories.length > 0) {
459
+ memoryContext += '=== Core Knowledge ===\n';
460
+ memoryContext += coreMemories
461
+ .map(memory => {
462
+ const type = memory.memory_type || 'general';
463
+ const typeLabel = type === 'fact'
464
+ ? 'Fact'
465
+ : type === 'preference'
466
+ ? 'Preference'
467
+ : type === 'instruction'
468
+ ? 'Instruction'
469
+ : 'Info';
470
+ return `[${typeLabel}] ${memory.content}`;
471
+ })
472
+ .join('\n');
473
+ memoryContext += '\n\n';
474
+ }
475
+ // Contextual memories section (relevant to current query)
476
+ if (contextualMemories.length > 0) {
477
+ memoryContext += '=== Relevant Context ===\n';
478
+ memoryContext += contextualMemories
479
+ .map(memory => {
480
+ const relevance = (memory.similarity_score * 100).toFixed(0);
481
+ return `[${relevance}% match] ${memory.entry.content}`;
482
+ })
483
+ .join('\n');
484
+ }
485
+ const enhancedSystemPrompt = `${baseSystemPrompt}
486
+
487
+ [PERSONA MEMORY CONTEXT]
488
+ You have access to the following memories from past interactions with this user:
489
+
490
+ ${memoryContext.trim()}
491
+
492
+ Guidelines:
493
+ - Use Core Knowledge naturally in your responses - these are established facts about the user
494
+ - Reference Relevant Context when it helps answer the current question
495
+ - Don't explicitly mention having "memories" - integrate knowledge seamlessly
496
+ - If memories conflict with current information, prioritize the most recent
497
+ [END MEMORY CONTEXT]`;
498
+ // Update the system message
499
+ const systemMessageIndex = session.messages.findIndex(msg => msg.role === 'system');
500
+ if (systemMessageIndex !== -1) {
501
+ session.messages[systemMessageIndex] = {
502
+ ...session.messages[systemMessageIndex],
503
+ content: enhancedSystemPrompt,
504
+ timestamp: Date.now(),
505
+ };
506
+ }
507
+ else {
508
+ // Add new system message
509
+ const systemMessage = {
510
+ id: uuidv4(),
511
+ role: 'system',
512
+ content: enhancedSystemPrompt,
513
+ timestamp: Date.now(),
514
+ };
515
+ session.messages.unshift(systemMessage);
516
+ }
517
+ // Save the updated session
518
+ this.sessions.set(session.id, session);
519
+ storageService.saveSession(session, userId);
520
+ console.log(`[ADVANCED] Updated system message with ${coreMemories.length} core + ${contextualMemories.length} contextual memories`);
521
+ }
522
+ catch (error) {
523
+ console.error(`[ADVANCED] Error updating system message with memories:`, error);
524
+ }
525
+ }
526
+ /**
527
+ * Process advanced persona response - store AI responses as memories for future reference
528
+ */
529
+ async processAdvancedPersonaResponse(personaId, userId, assistantMessage) {
530
+ try {
531
+ console.log(`[ADVANCED-DEBUG] processAdvancedPersonaResponse called - personaId: ${personaId}, userId: ${userId}`);
532
+ // Check if persona has advanced features enabled for current user only (no fallback to maintain privacy)
533
+ const persona = await personaService.getPersonaById(personaId, userId);
534
+ console.log(`[ADVANCED-DEBUG] Persona lookup for user ${userId}:`, persona ? `Found: ${persona.name}` : 'Not found');
535
+ if (!persona) {
536
+ console.log(`[ADVANCED-DEBUG] No persona found for user, exiting`);
537
+ return;
538
+ }
539
+ const hasAdvancedFeatures = persona.embedding_model || persona.memory_settings;
540
+ console.log(`[ADVANCED-DEBUG] Advanced detection check:`, {
541
+ embedding_model: persona.embedding_model,
542
+ memory_settings: persona.memory_settings,
543
+ hasAdvancedFeatures,
544
+ });
545
+ if (!hasAdvancedFeatures) {
546
+ console.log(`[ADVANCED-DEBUG] Not an advanced persona, exiting`);
547
+ return;
548
+ }
549
+ console.log(`[ADVANCED] Storing assistant response as memory for persona ${persona.name}`);
550
+ // Get advanced settings
551
+ const embeddingModel = persona.embedding_model || 'nomic-embed-text';
552
+ // Store the assistant response as a memory for future context
553
+ await memoryService.storeMemory(userId, personaId, `Assistant response: ${assistantMessage}`, embeddingModel, undefined, // context
554
+ 0.6 // slightly lower importance than user messages
555
+ );
556
+ console.log(`[ADVANCED] ✅ Assistant response stored successfully`);
557
+ }
558
+ catch (error) {
559
+ console.error(`[ADVANCED] Error processing persona response:`, error);
560
+ }
561
+ }
562
+ /**
563
+ * Create a new branch for a message (used for regeneration)
564
+ * This marks the original message as inactive and creates a new active variant
565
+ */
566
+ createMessageBranch(sessionId, originalMessageId, newMessage, userId = 'default') {
567
+ const session = this.getSession(sessionId, userId);
568
+ if (!session)
569
+ return undefined;
570
+ const originalMessage = session.messages.find(msg => msg.id === originalMessageId);
571
+ if (!originalMessage)
572
+ return undefined;
573
+ // The parent is either the original's parent (if it's already a variant) or the original itself
574
+ const parentId = originalMessage.parentId || originalMessageId;
575
+ // Find all siblings to determine the new branch index
576
+ const siblings = session.messages.filter(msg => msg.id === parentId || msg.parentId === parentId);
577
+ const newBranchIndex = siblings.length;
578
+ // Mark all current siblings as inactive
579
+ for (const sibling of siblings) {
580
+ sibling.isActive = false;
581
+ }
582
+ const messageId = newMessage.id || uuidv4();
583
+ const newBranchMessage = {
584
+ ...newMessage,
585
+ id: messageId,
586
+ timestamp: Date.now(),
587
+ parentId: parentId,
588
+ branchIndex: newBranchIndex,
589
+ isActive: true,
590
+ siblingCount: newBranchIndex + 1,
591
+ };
592
+ // Update sibling counts for all related messages
593
+ for (const sibling of siblings) {
594
+ sibling.siblingCount = newBranchIndex + 1;
595
+ }
596
+ session.messages.push(newBranchMessage);
597
+ session.updatedAt = Date.now();
598
+ this.sessions.set(sessionId, session);
599
+ storageService.saveSession(session, userId);
600
+ return newBranchMessage;
601
+ }
602
+ /**
603
+ * Switch to a different branch of a message
604
+ */
605
+ switchMessageBranch(sessionId, messageId, targetBranchIndex, userId = 'default') {
606
+ const session = this.getSession(sessionId, userId);
607
+ if (!session)
608
+ return undefined;
609
+ // Find the target message directly by ID
610
+ const targetMessage = session.messages.find(msg => msg.id === messageId);
611
+ if (!targetMessage)
612
+ return undefined;
613
+ // Find the parent ID (the original message that spawned branches)
614
+ const parentId = targetMessage.parentId || messageId;
615
+ // Find all siblings (including the original parent message)
616
+ const siblings = session.messages.filter(msg => msg.id === parentId || msg.parentId === parentId);
617
+ // Mark all siblings as inactive, then mark the target as active
618
+ for (const sibling of siblings) {
619
+ sibling.isActive = false;
620
+ }
621
+ targetMessage.isActive = true;
622
+ session.updatedAt = Date.now();
623
+ this.sessions.set(sessionId, session);
624
+ storageService.saveSession(session, userId);
625
+ return targetMessage;
626
+ }
627
+ /**
628
+ * Get all branches for a message
629
+ */
630
+ getMessageBranches(sessionId, messageId, userId = 'default') {
631
+ const session = this.getSession(sessionId, userId);
632
+ if (!session)
633
+ return [];
634
+ const message = session.messages.find(msg => msg.id === messageId);
635
+ if (!message)
636
+ return [];
637
+ const parentId = message.parentId || messageId;
638
+ // Find all siblings (including the original parent message)
639
+ return session.messages
640
+ .filter(msg => msg.id === parentId || msg.parentId === parentId)
641
+ .sort((a, b) => (a.branchIndex ?? 0) - (b.branchIndex ?? 0));
642
+ }
643
+ }
644
+ export default new ChatService();
645
+ //# sourceMappingURL=chatService.js.map