mem-llm 1.0.2__py3-none-any.whl → 2.1.0__py3-none-any.whl

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.

Potentially problematic release.


This version of mem-llm might be problematic. Click here for more details.

Files changed (41) hide show
  1. mem_llm/__init__.py +71 -8
  2. mem_llm/api_server.py +595 -0
  3. mem_llm/base_llm_client.py +201 -0
  4. mem_llm/builtin_tools.py +311 -0
  5. mem_llm/builtin_tools_async.py +170 -0
  6. mem_llm/cli.py +254 -0
  7. mem_llm/clients/__init__.py +22 -0
  8. mem_llm/clients/lmstudio_client.py +393 -0
  9. mem_llm/clients/ollama_client.py +354 -0
  10. mem_llm/config.yaml.example +1 -1
  11. mem_llm/config_from_docs.py +1 -1
  12. mem_llm/config_manager.py +5 -3
  13. mem_llm/conversation_summarizer.py +372 -0
  14. mem_llm/data_export_import.py +640 -0
  15. mem_llm/dynamic_prompt.py +298 -0
  16. mem_llm/llm_client.py +77 -14
  17. mem_llm/llm_client_factory.py +260 -0
  18. mem_llm/logger.py +129 -0
  19. mem_llm/mem_agent.py +1178 -87
  20. mem_llm/memory_db.py +290 -59
  21. mem_llm/memory_manager.py +60 -1
  22. mem_llm/prompt_security.py +304 -0
  23. mem_llm/response_metrics.py +221 -0
  24. mem_llm/retry_handler.py +193 -0
  25. mem_llm/thread_safe_db.py +301 -0
  26. mem_llm/tool_system.py +537 -0
  27. mem_llm/vector_store.py +278 -0
  28. mem_llm/web_launcher.py +129 -0
  29. mem_llm/web_ui/README.md +44 -0
  30. mem_llm/web_ui/__init__.py +7 -0
  31. mem_llm/web_ui/index.html +641 -0
  32. mem_llm/web_ui/memory.html +569 -0
  33. mem_llm/web_ui/metrics.html +75 -0
  34. mem_llm-2.1.0.dist-info/METADATA +753 -0
  35. mem_llm-2.1.0.dist-info/RECORD +40 -0
  36. {mem_llm-1.0.2.dist-info → mem_llm-2.1.0.dist-info}/WHEEL +1 -1
  37. mem_llm-2.1.0.dist-info/entry_points.txt +3 -0
  38. mem_llm/prompt_templates.py +0 -244
  39. mem_llm-1.0.2.dist-info/METADATA +0 -382
  40. mem_llm-1.0.2.dist-info/RECORD +0 -15
  41. {mem_llm-1.0.2.dist-info → mem_llm-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,641 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mem-LLM - Memory-Enabled AI Assistant</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ justify-content: center;
20
+ align-items: center;
21
+ padding: 20px;
22
+ }
23
+
24
+ .container {
25
+ max-width: 1400px;
26
+ width: 100%;
27
+ background: white;
28
+ border-radius: 20px;
29
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
30
+ overflow: hidden;
31
+ display: flex;
32
+ height: 90vh;
33
+ }
34
+
35
+ .sidebar {
36
+ width: 300px;
37
+ background: #2c3e50;
38
+ color: white;
39
+ padding: 30px 20px;
40
+ display: flex;
41
+ flex-direction: column;
42
+ overflow-y: auto;
43
+ }
44
+
45
+ .sidebar h1 {
46
+ font-size: 24px;
47
+ margin-bottom: 10px;
48
+ color: #3498db;
49
+ }
50
+
51
+ .sidebar p {
52
+ font-size: 12px;
53
+ opacity: 0.7;
54
+ margin-bottom: 30px;
55
+ }
56
+
57
+ .section {
58
+ margin-bottom: 25px;
59
+ }
60
+
61
+ .section-title {
62
+ font-size: 11px;
63
+ text-transform: uppercase;
64
+ opacity: 0.6;
65
+ margin-bottom: 10px;
66
+ letter-spacing: 1px;
67
+ }
68
+
69
+ .input-group {
70
+ margin-bottom: 15px;
71
+ }
72
+
73
+ .input-group label {
74
+ display: block;
75
+ font-size: 12px;
76
+ margin-bottom: 5px;
77
+ opacity: 0.8;
78
+ }
79
+
80
+ .input-group input,
81
+ .input-group select {
82
+ width: 100%;
83
+ padding: 10px;
84
+ border: none;
85
+ border-radius: 5px;
86
+ background: #34495e;
87
+ color: white;
88
+ font-size: 14px;
89
+ }
90
+
91
+ .input-group select option {
92
+ background: #34495e;
93
+ }
94
+
95
+ .input-group input:focus,
96
+ .input-group select:focus {
97
+ outline: 2px solid #3498db;
98
+ }
99
+
100
+ .stats {
101
+ margin-top: auto;
102
+ padding-top: 20px;
103
+ border-top: 1px solid #34495e;
104
+ }
105
+
106
+ .stat-item {
107
+ margin-bottom: 15px;
108
+ }
109
+
110
+ .stat-label {
111
+ font-size: 11px;
112
+ opacity: 0.6;
113
+ margin-bottom: 3px;
114
+ }
115
+
116
+ .stat-value {
117
+ font-size: 18px;
118
+ font-weight: bold;
119
+ color: #3498db;
120
+ }
121
+
122
+ .main-content {
123
+ flex: 1;
124
+ display: flex;
125
+ flex-direction: column;
126
+ }
127
+
128
+ .chat-header {
129
+ padding: 20px 30px;
130
+ background: #f8f9fa;
131
+ border-bottom: 1px solid #e9ecef;
132
+ display: flex;
133
+ justify-content: space-between;
134
+ align-items: center;
135
+ }
136
+
137
+ .chat-header h2 {
138
+ font-size: 20px;
139
+ color: #2c3e50;
140
+ }
141
+
142
+ .status {
143
+ font-size: 12px;
144
+ color: #27ae60;
145
+ margin-top: 5px;
146
+ }
147
+
148
+ .status.disconnected {
149
+ color: #e74c3c;
150
+ }
151
+
152
+ .config-info {
153
+ font-size: 11px;
154
+ color: #7f8c8d;
155
+ margin-top: 5px;
156
+ }
157
+
158
+ .chat-messages {
159
+ flex: 1;
160
+ overflow-y: auto;
161
+ padding: 30px;
162
+ background: #f8f9fa;
163
+ }
164
+
165
+ .message {
166
+ display: flex;
167
+ margin-bottom: 20px;
168
+ animation: fadeIn 0.3s;
169
+ }
170
+
171
+ @keyframes fadeIn {
172
+ from { opacity: 0; transform: translateY(10px); }
173
+ to { opacity: 1; transform: translateY(0); }
174
+ }
175
+
176
+ .message.user {
177
+ justify-content: flex-end;
178
+ }
179
+
180
+ .message-content {
181
+ max-width: 70%;
182
+ padding: 15px 20px;
183
+ border-radius: 15px;
184
+ position: relative;
185
+ }
186
+
187
+ .message.user .message-content {
188
+ background: #667eea;
189
+ color: white;
190
+ border-bottom-right-radius: 5px;
191
+ }
192
+
193
+ .message.bot .message-content {
194
+ background: white;
195
+ color: #2c3e50;
196
+ border-bottom-left-radius: 5px;
197
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
198
+ }
199
+
200
+ .message-time {
201
+ font-size: 10px;
202
+ opacity: 0.6;
203
+ margin-top: 5px;
204
+ }
205
+
206
+ .chat-input {
207
+ padding: 20px 30px;
208
+ background: white;
209
+ border-top: 1px solid #e9ecef;
210
+ display: flex;
211
+ gap: 10px;
212
+ }
213
+
214
+ .chat-input input {
215
+ flex: 1;
216
+ padding: 15px 20px;
217
+ border: 2px solid #e9ecef;
218
+ border-radius: 25px;
219
+ font-size: 14px;
220
+ transition: border-color 0.3s;
221
+ }
222
+
223
+ .chat-input input:focus {
224
+ outline: none;
225
+ border-color: #667eea;
226
+ }
227
+
228
+ .chat-input button {
229
+ padding: 15px 30px;
230
+ background: #667eea;
231
+ color: white;
232
+ border: none;
233
+ border-radius: 25px;
234
+ font-size: 14px;
235
+ font-weight: bold;
236
+ cursor: pointer;
237
+ transition: background 0.3s;
238
+ }
239
+
240
+ .chat-input button:hover {
241
+ background: #5568d3;
242
+ }
243
+
244
+ .chat-input button:disabled {
245
+ background: #95a5a6;
246
+ cursor: not-allowed;
247
+ }
248
+
249
+ .welcome-message {
250
+ text-align: center;
251
+ padding: 50px 20px;
252
+ color: #7f8c8d;
253
+ }
254
+
255
+ .welcome-message h3 {
256
+ font-size: 24px;
257
+ margin-bottom: 10px;
258
+ color: #2c3e50;
259
+ }
260
+
261
+ .welcome-message p {
262
+ font-size: 14px;
263
+ }
264
+
265
+ .btn-reconnect {
266
+ margin-top: 10px;
267
+ padding: 8px 16px;
268
+ background: #3498db;
269
+ color: white;
270
+ border: none;
271
+ border-radius: 5px;
272
+ cursor: pointer;
273
+ font-size: 12px;
274
+ }
275
+
276
+ .btn-reconnect:hover {
277
+ background: #2980b9;
278
+ }
279
+
280
+ .btn {
281
+ padding: 10px 20px;
282
+ background: #3498db;
283
+ color: white;
284
+ border: none;
285
+ border-radius: 5px;
286
+ cursor: pointer;
287
+ text-decoration: none;
288
+ font-size: 14px;
289
+ display: inline-block;
290
+ }
291
+
292
+ .btn:hover {
293
+ background: #2980b9;
294
+ }
295
+
296
+ @media (max-width: 768px) {
297
+ .sidebar {
298
+ width: 200px;
299
+ }
300
+
301
+ .message-content {
302
+ max-width: 85%;
303
+ }
304
+ }
305
+ </style>
306
+ </head>
307
+ <body>
308
+ <div class="container">
309
+ <!-- Sidebar -->
310
+ <div class="sidebar">
311
+ <div>
312
+ <h1>🧠 Mem-LLM</h1>
313
+ <p>Memory-Enabled AI Assistant v1.3.3</p>
314
+ </div>
315
+
316
+ <!-- User Settings -->
317
+ <div class="section">
318
+ <div class="section-title">👤 User</div>
319
+ <div class="input-group">
320
+ <label>User ID</label>
321
+ <input type="text" id="userId" value="user123" placeholder="Enter your user ID">
322
+ </div>
323
+ </div>
324
+
325
+ <!-- Backend Settings -->
326
+ <div class="section">
327
+ <div class="section-title">🔌 Backend Settings</div>
328
+
329
+ <div class="input-group">
330
+ <label>Backend</label>
331
+ <select id="backend">
332
+ <option value="ollama">Ollama (Local)</option>
333
+ <option value="lmstudio">LM Studio (Local)</option>
334
+ </select>
335
+ </div>
336
+
337
+ <div class="input-group">
338
+ <label>Model</label>
339
+ <input type="text" id="model" value="granite4:3b" placeholder="Model name">
340
+ </div>
341
+
342
+ <div class="input-group">
343
+ <label>API URL</label>
344
+ <input type="text" id="apiUrl" value="http://localhost:8000" placeholder="API Server URL">
345
+ </div>
346
+ </div>
347
+
348
+ <!-- Memory Settings -->
349
+ <div class="section">
350
+ <div class="section-title">💾 Memory Settings</div>
351
+
352
+ <div class="input-group">
353
+ <label>
354
+ <input type="checkbox" id="useSQL" checked style="width: auto; margin-right: 5px;">
355
+ Use SQL Memory
356
+ </label>
357
+ </div>
358
+
359
+ <div class="input-group">
360
+ <label>
361
+ <input type="checkbox" id="loadKB" checked style="width: auto; margin-right: 5px;">
362
+ Load Knowledge Base
363
+ </label>
364
+ </div>
365
+ </div>
366
+
367
+ <!-- Stats -->
368
+ <div class="stats">
369
+ <div class="stat-item">
370
+ <div class="stat-label">Messages</div>
371
+ <div class="stat-value" id="messageCount">0</div>
372
+ </div>
373
+ <div class="stat-item">
374
+ <div class="stat-label">Session Time</div>
375
+ <div class="stat-value" id="sessionTime">0m</div>
376
+ </div>
377
+ <div class="stat-item">
378
+ <div class="stat-label">Backend</div>
379
+ <div class="stat-value" id="currentBackend">-</div>
380
+ </div>
381
+ </div>
382
+ </div>
383
+
384
+ <!-- Main Chat Area -->
385
+ <div class="main-content">
386
+ <div class="chat-header">
387
+ <div>
388
+ <h2>💬 Chat</h2>
389
+ <div class="status" id="status">● Connecting...</div>
390
+ <div class="config-info" id="configInfo">Configure settings and connect</div>
391
+ </div>
392
+ <div style="display: flex; gap: 10px;">
393
+ <a href="memory.html" class="btn">🧠 Memory</a>
394
+ <a href="metrics.html" class="btn">📊 Metrics</a>
395
+ </div>
396
+ </div>
397
+
398
+ <div class="chat-messages" id="chatMessages">
399
+ <div class="welcome-message">
400
+ <h3>👋 Welcome!</h3>
401
+ <p>How can Mem-LLM help you today?</p>
402
+ <p style="margin-top: 10px; font-size: 12px;">
403
+ I'm an AI assistant with memory. I can remember our conversations! 🧠
404
+ </p>
405
+ <p style="margin-top: 15px; font-size: 11px; color: #95a5a6;">
406
+ Configure backend and model settings on the left
407
+ </p>
408
+ <button class="btn-reconnect" onclick="connect()">Connect</button>
409
+ </div>
410
+ </div>
411
+
412
+ <div class="chat-input">
413
+ <input type="text" id="messageInput" placeholder="Type your message..." autocomplete="off">
414
+ <button id="sendButton">Send</button>
415
+ </div>
416
+ </div>
417
+ </div>
418
+
419
+ <script>
420
+ // WebSocket connection
421
+ let ws = null;
422
+ let messageCount = 0;
423
+ let sessionStart = Date.now();
424
+
425
+ // DOM elements
426
+ const chatMessages = document.getElementById('chatMessages');
427
+ const messageInput = document.getElementById('messageInput');
428
+ const sendButton = document.getElementById('sendButton');
429
+ const userIdInput = document.getElementById('userId');
430
+ const backendSelect = document.getElementById('backend');
431
+ const modelInput = document.getElementById('model');
432
+ const apiUrlInput = document.getElementById('apiUrl');
433
+ const useSQLCheckbox = document.getElementById('useSQL');
434
+ const loadKBCheckbox = document.getElementById('loadKB');
435
+ const statusElement = document.getElementById('status');
436
+ const configInfoElement = document.getElementById('configInfo');
437
+ const messageCountElement = document.getElementById('messageCount');
438
+ const sessionTimeElement = document.getElementById('sessionTime');
439
+ const currentBackendElement = document.getElementById('currentBackend');
440
+
441
+ // Update model when backend changes
442
+ backendSelect.addEventListener('change', () => {
443
+ const backend = backendSelect.value;
444
+ if (backend === 'ollama') {
445
+ modelInput.value = 'granite4:3b';
446
+ } else if (backend === 'lmstudio') {
447
+ modelInput.value = 'qwen/qwen3-vl-4b';
448
+ }
449
+ });
450
+
451
+ // Connect to WebSocket
452
+ function connect() {
453
+ const userId = userIdInput.value || 'user123';
454
+ const apiUrl = apiUrlInput.value || 'http://localhost:8000';
455
+
456
+ // Extract host from API URL
457
+ const url = new URL(apiUrl);
458
+ const wsUrl = `ws://${url.host}/ws/chat/${userId}`;
459
+
460
+ statusElement.textContent = '● Connecting...';
461
+ statusElement.className = 'status';
462
+
463
+ try {
464
+ ws = new WebSocket(wsUrl);
465
+
466
+ ws.onopen = () => {
467
+ console.log('Connected to Mem-LLM');
468
+ statusElement.textContent = '● Connected';
469
+ statusElement.className = 'status';
470
+
471
+ const backend = backendSelect.value;
472
+ const model = modelInput.value;
473
+ const useSQL = useSQLCheckbox.checked;
474
+ const loadKB = loadKBCheckbox.checked;
475
+
476
+ currentBackendElement.textContent = backend.toUpperCase();
477
+ configInfoElement.textContent = `Model: ${model} | SQL: ${useSQL ? 'On' : 'Off'} | KB: ${loadKB ? 'On' : 'Off'}`;
478
+
479
+ // Send initial configuration
480
+ sendConfigToAPI();
481
+ };
482
+
483
+ ws.onclose = () => {
484
+ console.log('Disconnected from Mem-LLM');
485
+ statusElement.textContent = '● Disconnected';
486
+ statusElement.className = 'status disconnected';
487
+ configInfoElement.textContent = 'Click "Connect" button to reconnect';
488
+ };
489
+
490
+ ws.onerror = (error) => {
491
+ console.error('WebSocket error:', error);
492
+ statusElement.textContent = '● Error';
493
+ statusElement.className = 'status disconnected';
494
+ configInfoElement.textContent = `Connection error. Is API Server running? (${apiUrl})`;
495
+ };
496
+
497
+ ws.onmessage = (event) => {
498
+ const data = JSON.parse(event.data);
499
+ handleServerMessage(data);
500
+ };
501
+ } catch (error) {
502
+ console.error('Connection error:', error);
503
+ statusElement.textContent = '● Error';
504
+ statusElement.className = 'status disconnected';
505
+ configInfoElement.textContent = `Connection error: ${error.message}`;
506
+ }
507
+ }
508
+
509
+ // Send configuration to API
510
+ async function sendConfigToAPI() {
511
+ const userId = userIdInput.value || 'user123';
512
+ const apiUrl = apiUrlInput.value || 'http://localhost:8000';
513
+ const backend = backendSelect.value;
514
+ const model = modelInput.value;
515
+
516
+ try {
517
+ const response = await fetch(`${apiUrl}/api/v1/agent/configure/${userId}`, {
518
+ method: 'POST',
519
+ headers: {
520
+ 'Content-Type': 'application/json',
521
+ },
522
+ body: JSON.stringify({
523
+ model: model,
524
+ backend: backend,
525
+ base_url: backend === 'ollama' ? 'http://localhost:11434' : 'http://localhost:1234'
526
+ })
527
+ });
528
+
529
+ if (response.ok) {
530
+ console.log('Configuration sent successfully');
531
+ } else {
532
+ console.error('Configuration failed:', response.status);
533
+ }
534
+ } catch (error) {
535
+ console.error('Configuration error:', error);
536
+ }
537
+ }
538
+
539
+ // Handle messages from server
540
+ let currentBotMessage = null;
541
+
542
+ function handleServerMessage(data) {
543
+ if (data.type === 'start') {
544
+ // Start of streaming response
545
+ currentBotMessage = addMessage('', 'bot');
546
+ } else if (data.type === 'chunk') {
547
+ // Streaming chunk
548
+ if (currentBotMessage) {
549
+ currentBotMessage.textContent += data.content;
550
+ scrollToBottom();
551
+ }
552
+ } else if (data.type === 'done') {
553
+ // End of streaming
554
+ currentBotMessage = null;
555
+ } else if (data.type === 'error') {
556
+ addMessage(`⚠️ Error: ${data.content}`, 'bot');
557
+ }
558
+ }
559
+
560
+ // Add message to chat
561
+ function addMessage(text, type) {
562
+ // Remove welcome message if exists
563
+ const welcomeMsg = chatMessages.querySelector('.welcome-message');
564
+ if (welcomeMsg) {
565
+ welcomeMsg.remove();
566
+ }
567
+
568
+ const messageDiv = document.createElement('div');
569
+ messageDiv.className = `message ${type}`;
570
+
571
+ const contentDiv = document.createElement('div');
572
+ contentDiv.className = 'message-content';
573
+
574
+ const textDiv = document.createElement('div');
575
+ textDiv.textContent = text;
576
+ contentDiv.appendChild(textDiv);
577
+
578
+ const timeDiv = document.createElement('div');
579
+ timeDiv.className = 'message-time';
580
+ timeDiv.textContent = new Date().toLocaleTimeString();
581
+ contentDiv.appendChild(timeDiv);
582
+
583
+ messageDiv.appendChild(contentDiv);
584
+ chatMessages.appendChild(messageDiv);
585
+
586
+ scrollToBottom();
587
+
588
+ return textDiv; // Return text element for streaming
589
+ }
590
+
591
+ // Scroll to bottom
592
+ function scrollToBottom() {
593
+ chatMessages.scrollTop = chatMessages.scrollHeight;
594
+ }
595
+
596
+ // Send message
597
+ function sendMessage() {
598
+ const message = messageInput.value.trim();
599
+ if (!message || !ws || ws.readyState !== WebSocket.OPEN) {
600
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
601
+ alert('Please connect first! (Click Connect button)');
602
+ }
603
+ return;
604
+ }
605
+
606
+ // Add user message to UI
607
+ addMessage(message, 'user');
608
+ messageInput.value = '';
609
+
610
+ // Send to server
611
+ ws.send(JSON.stringify({
612
+ message: message,
613
+ metadata: {
614
+ timestamp: new Date().toISOString(),
615
+ backend: backendSelect.value,
616
+ model: modelInput.value
617
+ }
618
+ }));
619
+
620
+ // Update stats
621
+ messageCount++;
622
+ messageCountElement.textContent = messageCount;
623
+ }
624
+
625
+ // Event listeners
626
+ sendButton.addEventListener('click', sendMessage);
627
+ messageInput.addEventListener('keypress', (e) => {
628
+ if (e.key === 'Enter') {
629
+ sendMessage();
630
+ }
631
+ });
632
+
633
+ // Update session time every minute
634
+ setInterval(() => {
635
+ const minutes = Math.floor((Date.now() - sessionStart) / 60000);
636
+ sessionTimeElement.textContent = `${minutes}m`;
637
+ }, 60000);
638
+ </script>
639
+ </body>
640
+ </html>
641
+