mem-llm 1.0.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.

@@ -0,0 +1,376 @@
1
+ """
2
+ SQL Database Memory Management
3
+ Stores memory data using SQLite - Production-ready
4
+ """
5
+
6
+ import sqlite3
7
+ import json
8
+ from datetime import datetime
9
+ from typing import Dict, List, Optional, Tuple
10
+ from pathlib import Path
11
+
12
+
13
+ class SQLMemoryManager:
14
+ """SQLite-based memory management system"""
15
+
16
+ def __init__(self, db_path: str = "memories.db"):
17
+ """
18
+ Args:
19
+ db_path: SQLite database file path
20
+ """
21
+ self.db_path = Path(db_path)
22
+ self.conn = None
23
+ self._init_database()
24
+
25
+ def _init_database(self) -> None:
26
+ """Create database and tables"""
27
+ self.conn = sqlite3.connect(str(self.db_path), check_same_thread=False)
28
+ self.conn.row_factory = sqlite3.Row
29
+
30
+ cursor = self.conn.cursor()
31
+
32
+ # User profiles table
33
+ cursor.execute("""
34
+ CREATE TABLE IF NOT EXISTS users (
35
+ user_id TEXT PRIMARY KEY,
36
+ name TEXT,
37
+ first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
38
+ last_interaction TIMESTAMP,
39
+ preferences TEXT,
40
+ summary TEXT,
41
+ metadata TEXT
42
+ )
43
+ """)
44
+
45
+ # Conversations table
46
+ cursor.execute("""
47
+ CREATE TABLE IF NOT EXISTS conversations (
48
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49
+ user_id TEXT NOT NULL,
50
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51
+ user_message TEXT NOT NULL,
52
+ bot_response TEXT NOT NULL,
53
+ metadata TEXT,
54
+ sentiment TEXT,
55
+ resolved BOOLEAN DEFAULT 0,
56
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
57
+ )
58
+ """)
59
+
60
+ # İndeksler - Performans için
61
+ cursor.execute("""
62
+ CREATE INDEX IF NOT EXISTS idx_user_timestamp
63
+ ON conversations(user_id, timestamp DESC)
64
+ """)
65
+
66
+ cursor.execute("""
67
+ CREATE INDEX IF NOT EXISTS idx_resolved
68
+ ON conversations(user_id, resolved)
69
+ """)
70
+
71
+ # Senaryo şablonları tablosu
72
+ cursor.execute("""
73
+ CREATE TABLE IF NOT EXISTS scenario_templates (
74
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
75
+ name TEXT NOT NULL UNIQUE,
76
+ description TEXT,
77
+ system_prompt TEXT NOT NULL,
78
+ example_interactions TEXT,
79
+ metadata TEXT,
80
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
81
+ )
82
+ """)
83
+
84
+ # Problem/FAQ veritabanı
85
+ cursor.execute("""
86
+ CREATE TABLE IF NOT EXISTS knowledge_base (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ category TEXT NOT NULL,
89
+ question TEXT NOT NULL,
90
+ answer TEXT NOT NULL,
91
+ keywords TEXT,
92
+ priority INTEGER DEFAULT 0,
93
+ active BOOLEAN DEFAULT 1,
94
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
95
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
96
+ )
97
+ """)
98
+
99
+ cursor.execute("""
100
+ CREATE INDEX IF NOT EXISTS idx_category
101
+ ON knowledge_base(category, active)
102
+ """)
103
+
104
+ self.conn.commit()
105
+
106
+ def add_user(self, user_id: str, name: Optional[str] = None,
107
+ metadata: Optional[Dict] = None) -> None:
108
+ """
109
+ Add new user or update existing
110
+
111
+ Args:
112
+ user_id: User ID
113
+ name: User name
114
+ metadata: Additional information
115
+ """
116
+ cursor = self.conn.cursor()
117
+ cursor.execute("""
118
+ INSERT INTO users (user_id, name, metadata)
119
+ VALUES (?, ?, ?)
120
+ ON CONFLICT(user_id) DO UPDATE SET
121
+ name = COALESCE(excluded.name, users.name),
122
+ metadata = COALESCE(excluded.metadata, users.metadata)
123
+ """, (user_id, name, json.dumps(metadata or {})))
124
+ self.conn.commit()
125
+
126
+ def add_interaction(self, user_id: str, user_message: str,
127
+ bot_response: str, metadata: Optional[Dict] = None,
128
+ resolved: bool = False) -> int:
129
+ """
130
+ Record new interaction
131
+
132
+ Args:
133
+ user_id: User ID
134
+ user_message: User's message
135
+ bot_response: Bot's response
136
+ metadata: Additional information
137
+ resolved: Is issue resolved?
138
+
139
+ Returns:
140
+ Added record ID
141
+ """
142
+ cursor = self.conn.cursor()
143
+
144
+ # Create user if not exists
145
+ self.add_user(user_id)
146
+
147
+ # Record interaction
148
+ cursor.execute("""
149
+ INSERT INTO conversations
150
+ (user_id, user_message, bot_response, metadata, resolved)
151
+ VALUES (?, ?, ?, ?, ?)
152
+ """, (user_id, user_message, bot_response,
153
+ json.dumps(metadata or {}), resolved))
154
+
155
+ interaction_id = cursor.lastrowid
156
+
157
+ # Update user's last interaction time
158
+ cursor.execute("""
159
+ UPDATE users
160
+ SET last_interaction = CURRENT_TIMESTAMP
161
+ WHERE user_id = ?
162
+ """, (user_id,))
163
+
164
+ self.conn.commit()
165
+ return interaction_id
166
+
167
+ def get_recent_conversations(self, user_id: str, limit: int = 10) -> List[Dict]:
168
+ """
169
+ Kullanıcının son konuşmalarını getirir
170
+
171
+ Args:
172
+ user_id: Kullanıcı kimliği
173
+ limit: Getirilecek konuşma sayısı
174
+
175
+ Returns:
176
+ Konuşmalar listesi
177
+ """
178
+ cursor = self.conn.cursor()
179
+ cursor.execute("""
180
+ SELECT timestamp, user_message, bot_response, metadata, resolved
181
+ FROM conversations
182
+ WHERE user_id = ?
183
+ ORDER BY timestamp DESC
184
+ LIMIT ?
185
+ """, (user_id, limit))
186
+
187
+ rows = cursor.fetchall()
188
+ return [dict(row) for row in rows]
189
+
190
+ def search_conversations(self, user_id: str, keyword: str) -> List[Dict]:
191
+ """
192
+ Konuşmalarda anahtar kelime arar
193
+
194
+ Args:
195
+ user_id: Kullanıcı kimliği
196
+ keyword: Aranacak kelime
197
+
198
+ Returns:
199
+ Eşleşen konuşmalar
200
+ """
201
+ cursor = self.conn.cursor()
202
+ cursor.execute("""
203
+ SELECT timestamp, user_message, bot_response, metadata, resolved
204
+ FROM conversations
205
+ WHERE user_id = ?
206
+ AND (user_message LIKE ? OR bot_response LIKE ? OR metadata LIKE ?)
207
+ ORDER BY timestamp DESC
208
+ """, (user_id, f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"))
209
+
210
+ rows = cursor.fetchall()
211
+ return [dict(row) for row in rows]
212
+
213
+ def get_user_profile(self, user_id: str) -> Optional[Dict]:
214
+ """
215
+ Kullanıcı profilini getirir
216
+
217
+ Args:
218
+ user_id: Kullanıcı kimliği
219
+
220
+ Returns:
221
+ Kullanıcı profili veya None
222
+ """
223
+ cursor = self.conn.cursor()
224
+ cursor.execute("""
225
+ SELECT * FROM users WHERE user_id = ?
226
+ """, (user_id,))
227
+
228
+ row = cursor.fetchone()
229
+ if row:
230
+ return dict(row)
231
+ return None
232
+
233
+ def update_user_profile(self, user_id: str, updates: Dict) -> None:
234
+ """
235
+ Kullanıcı profilini günceller
236
+
237
+ Args:
238
+ user_id: Kullanıcı kimliği
239
+ updates: Güncellenecek alanlar
240
+ """
241
+ allowed_fields = ['name', 'preferences', 'summary', 'metadata']
242
+ set_clause = []
243
+ values = []
244
+
245
+ for field, value in updates.items():
246
+ if field in allowed_fields:
247
+ set_clause.append(f"{field} = ?")
248
+ if isinstance(value, (dict, list)):
249
+ values.append(json.dumps(value))
250
+ else:
251
+ values.append(value)
252
+
253
+ if set_clause:
254
+ values.append(user_id)
255
+ cursor = self.conn.cursor()
256
+ cursor.execute(f"""
257
+ UPDATE users
258
+ SET {', '.join(set_clause)}
259
+ WHERE user_id = ?
260
+ """, values)
261
+ self.conn.commit()
262
+
263
+ def add_knowledge(self, category: str, question: str, answer: str,
264
+ keywords: Optional[List[str]] = None,
265
+ priority: int = 0) -> int:
266
+ """
267
+ Bilgi bankasına yeni kayıt ekler
268
+
269
+ Args:
270
+ category: Kategori (örn: "kargo", "iade", "ödeme")
271
+ question: Soru
272
+ answer: Cevap
273
+ keywords: Anahtar kelimeler
274
+ priority: Öncelik (yüksek = önce gösterilir)
275
+
276
+ Returns:
277
+ Kayıt ID'si
278
+ """
279
+ cursor = self.conn.cursor()
280
+ cursor.execute("""
281
+ INSERT INTO knowledge_base
282
+ (category, question, answer, keywords, priority)
283
+ VALUES (?, ?, ?, ?, ?)
284
+ """, (category, question, answer,
285
+ json.dumps(keywords or []), priority))
286
+
287
+ self.conn.commit()
288
+ return cursor.lastrowid
289
+
290
+ def search_knowledge(self, query: str, category: Optional[str] = None,
291
+ limit: int = 5) -> List[Dict]:
292
+ """
293
+ Bilgi bankasında arama yapar
294
+
295
+ Args:
296
+ query: Arama sorgusu
297
+ category: Kategori filtresi (opsiyonel)
298
+ limit: Maksimum sonuç sayısı
299
+
300
+ Returns:
301
+ Bulunan kayıtlar
302
+ """
303
+ cursor = self.conn.cursor()
304
+
305
+ if category:
306
+ cursor.execute("""
307
+ SELECT category, question, answer, priority
308
+ FROM knowledge_base
309
+ WHERE active = 1
310
+ AND category = ?
311
+ AND (question LIKE ? OR answer LIKE ? OR keywords LIKE ?)
312
+ ORDER BY priority DESC, id DESC
313
+ LIMIT ?
314
+ """, (category, f"%{query}%", f"%{query}%", f"%{query}%", limit))
315
+ else:
316
+ cursor.execute("""
317
+ SELECT category, question, answer, priority
318
+ FROM knowledge_base
319
+ WHERE active = 1
320
+ AND (question LIKE ? OR answer LIKE ? OR keywords LIKE ?)
321
+ ORDER BY priority DESC, id DESC
322
+ LIMIT ?
323
+ """, (f"%{query}%", f"%{query}%", f"%{query}%", limit))
324
+
325
+ return [dict(row) for row in cursor.fetchall()]
326
+
327
+ def get_statistics(self) -> Dict:
328
+ """
329
+ Genel istatistikleri döndürür
330
+
331
+ Returns:
332
+ İstatistik bilgileri
333
+ """
334
+ cursor = self.conn.cursor()
335
+
336
+ # Toplam kullanıcı
337
+ cursor.execute("SELECT COUNT(*) as count FROM users")
338
+ total_users = cursor.fetchone()['count']
339
+
340
+ # Toplam etkileşim
341
+ cursor.execute("SELECT COUNT(*) as count FROM conversations")
342
+ total_interactions = cursor.fetchone()['count']
343
+
344
+ # Çözülmemiş sorunlar
345
+ cursor.execute("SELECT COUNT(*) as count FROM conversations WHERE resolved = 0")
346
+ unresolved = cursor.fetchone()['count']
347
+
348
+ # Bilgi bankası kayıt sayısı
349
+ cursor.execute("SELECT COUNT(*) as count FROM knowledge_base WHERE active = 1")
350
+ kb_count = cursor.fetchone()['count']
351
+
352
+ return {
353
+ "total_users": total_users,
354
+ "total_interactions": total_interactions,
355
+ "unresolved_issues": unresolved,
356
+ "knowledge_base_entries": kb_count,
357
+ "avg_interactions_per_user": total_interactions / total_users if total_users > 0 else 0
358
+ }
359
+
360
+ def clear_memory(self, user_id: str) -> None:
361
+ """Delete all user conversations"""
362
+ cursor = self.conn.cursor()
363
+ cursor.execute("DELETE FROM conversations WHERE user_id = ?", (user_id,))
364
+ self.conn.commit()
365
+
366
+ def close(self) -> None:
367
+ """Veritabanı bağlantısını kapatır"""
368
+ if self.conn:
369
+ self.conn.close()
370
+
371
+ def __enter__(self):
372
+ return self
373
+
374
+ def __exit__(self, exc_type, exc_val, exc_tb):
375
+ self.close()
376
+
@@ -0,0 +1,257 @@
1
+ """
2
+ Memory Manager - Memory Management System
3
+ Stores, updates and remembers user interactions.
4
+ """
5
+
6
+ import json
7
+ import os
8
+ from datetime import datetime
9
+ from typing import Dict, List, Optional
10
+ from pathlib import Path
11
+
12
+
13
+ class MemoryManager:
14
+ """Memory system that manages user interactions and context"""
15
+
16
+ def __init__(self, memory_dir: str = "memories"):
17
+ """
18
+ Args:
19
+ memory_dir: Directory where memory files will be stored
20
+ """
21
+ self.memory_dir = Path(memory_dir)
22
+ self.memory_dir.mkdir(exist_ok=True)
23
+ self.conversations: Dict[str, List[Dict]] = {}
24
+ self.user_profiles: Dict[str, Dict] = {}
25
+
26
+ def _get_user_file(self, user_id: str) -> Path:
27
+ """Returns the path of user's memory file"""
28
+ return self.memory_dir / f"{user_id}.json"
29
+
30
+ def load_memory(self, user_id: str) -> Dict:
31
+ """
32
+ Load user's memory
33
+
34
+ Args:
35
+ user_id: User ID
36
+
37
+ Returns:
38
+ User's memory data
39
+ """
40
+ user_file = self._get_user_file(user_id)
41
+
42
+ if user_file.exists():
43
+ with open(user_file, 'r', encoding='utf-8') as f:
44
+ data = json.load(f)
45
+ self.conversations[user_id] = data.get('conversations', [])
46
+ self.user_profiles[user_id] = data.get('profile', {})
47
+ return data
48
+ else:
49
+ # Create empty memory for new user
50
+ self.conversations[user_id] = []
51
+ self.user_profiles[user_id] = {
52
+ 'user_id': user_id,
53
+ 'first_seen': datetime.now().isoformat(),
54
+ 'preferences': {},
55
+ 'summary': {}
56
+ }
57
+ return {
58
+ 'conversations': [],
59
+ 'profile': self.user_profiles[user_id]
60
+ }
61
+
62
+ def save_memory(self, user_id: str) -> None:
63
+ """
64
+ Save user's memory to disk
65
+
66
+ Args:
67
+ user_id: User ID
68
+ """
69
+ user_file = self._get_user_file(user_id)
70
+
71
+ data = {
72
+ 'conversations': self.conversations.get(user_id, []),
73
+ 'profile': self.user_profiles.get(user_id, {}),
74
+ 'last_updated': datetime.now().isoformat()
75
+ }
76
+
77
+ with open(user_file, 'w', encoding='utf-8') as f:
78
+ json.dump(data, f, ensure_ascii=False, indent=2)
79
+
80
+ def add_interaction(self, user_id: str, user_message: str,
81
+ bot_response: str, metadata: Optional[Dict] = None) -> None:
82
+ """
83
+ Record a new interaction
84
+
85
+ Args:
86
+ user_id: User ID
87
+ user_message: User's message
88
+ bot_response: Bot's response
89
+ metadata: Additional information (order no, issue type, etc.)
90
+ """
91
+ if user_id not in self.conversations:
92
+ self.load_memory(user_id)
93
+
94
+ interaction = {
95
+ 'timestamp': datetime.now().isoformat(),
96
+ 'user_message': user_message,
97
+ 'bot_response': bot_response,
98
+ 'metadata': metadata or {}
99
+ }
100
+
101
+ self.conversations[user_id].append(interaction)
102
+ self.save_memory(user_id)
103
+
104
+ def update_profile(self, user_id: str, updates: Dict) -> None:
105
+ """
106
+ Update user profile
107
+
108
+ Args:
109
+ user_id: User ID
110
+ updates: Information to update
111
+ """
112
+ if user_id not in self.user_profiles:
113
+ self.load_memory(user_id)
114
+
115
+ self.user_profiles[user_id].update(updates)
116
+ self.save_memory(user_id)
117
+
118
+ def get_recent_conversations(self, user_id: str, limit: int = 5) -> List[Dict]:
119
+ """
120
+ Get last N conversations
121
+
122
+ Args:
123
+ user_id: User ID
124
+ limit: Number of conversations to retrieve
125
+
126
+ Returns:
127
+ List of recent conversations
128
+ """
129
+ if user_id not in self.conversations:
130
+ self.load_memory(user_id)
131
+
132
+ return self.conversations[user_id][-limit:]
133
+
134
+ def search_memory(self, user_id: str, keyword: str) -> List[Dict]:
135
+ """
136
+ Search for keyword in memory
137
+
138
+ Args:
139
+ user_id: User ID
140
+ keyword: Word to search for
141
+
142
+ Returns:
143
+ Matching interactions
144
+ """
145
+ if user_id not in self.conversations:
146
+ self.load_memory(user_id)
147
+
148
+ results = []
149
+ keyword_lower = keyword.lower()
150
+
151
+ for interaction in self.conversations[user_id]:
152
+ if (keyword_lower in interaction['user_message'].lower() or
153
+ keyword_lower in interaction['bot_response'].lower() or
154
+ keyword_lower in str(interaction.get('metadata', {})).lower()):
155
+ results.append(interaction)
156
+
157
+ return results
158
+
159
+ def get_summary(self, user_id: str) -> str:
160
+ """
161
+ Create summary of user's past interactions
162
+
163
+ Args:
164
+ user_id: User ID
165
+
166
+ Returns:
167
+ Summary text
168
+ """
169
+ if user_id not in self.conversations:
170
+ self.load_memory(user_id)
171
+
172
+ profile = self.user_profiles.get(user_id, {})
173
+ conversations = self.conversations.get(user_id, [])
174
+
175
+ if not conversations:
176
+ return "No interactions with this user yet."
177
+
178
+ summary_parts = [
179
+ f"User ID: {user_id}",
180
+ f"First conversation: {profile.get('first_seen', 'Unknown')}",
181
+ f"Total interactions: {len(conversations)}",
182
+ ]
183
+
184
+ # Add last 3 interactions
185
+ if conversations:
186
+ summary_parts.append("\nRecent interactions:")
187
+ for i, conv in enumerate(conversations[-3:], 1):
188
+ timestamp = conv.get('timestamp', 'Unknown')
189
+ user_msg = conv.get('user_message', '')[:50]
190
+ summary_parts.append(f"{i}. {timestamp}: {user_msg}...")
191
+
192
+ # Metadata summary
193
+ all_metadata = [c.get('metadata', {}) for c in conversations if c.get('metadata')]
194
+ if all_metadata:
195
+ summary_parts.append("\nSaved information:")
196
+ # Example: order numbers, issues, etc.
197
+ for meta in all_metadata[-3:]:
198
+ for key, value in meta.items():
199
+ summary_parts.append(f" - {key}: {value}")
200
+
201
+ return "\n".join(summary_parts)
202
+
203
+ def clear_memory(self, user_id: str) -> None:
204
+ """
205
+ Completely delete user's memory
206
+
207
+ Args:
208
+ user_id: User ID
209
+ """
210
+ user_file = self._get_user_file(user_id)
211
+ if user_file.exists():
212
+ user_file.unlink()
213
+
214
+ if user_id in self.conversations:
215
+ del self.conversations[user_id]
216
+ if user_id in self.user_profiles:
217
+ del self.user_profiles[user_id]
218
+
219
+ def search_conversations(self, user_id: str, keyword: str) -> List[Dict]:
220
+ """
221
+ Search for keyword in conversations (for JSON version)
222
+
223
+ Args:
224
+ user_id: User ID
225
+ keyword: Word to search for
226
+
227
+ Returns:
228
+ Matching conversations
229
+ """
230
+ if user_id not in self.conversations:
231
+ self.load_memory(user_id)
232
+
233
+ results = []
234
+ keyword_lower = keyword.lower()
235
+
236
+ for interaction in self.conversations.get(user_id, []):
237
+ if (keyword_lower in interaction['user_message'].lower() or
238
+ keyword_lower in interaction['bot_response'].lower()):
239
+ results.append(interaction)
240
+
241
+ return results
242
+
243
+ def get_user_profile(self, user_id: str) -> Optional[Dict]:
244
+ """
245
+ Get user profile (for JSON version)
246
+
247
+ Args:
248
+ user_id: User ID
249
+
250
+ Returns:
251
+ User profile or None
252
+ """
253
+ if user_id not in self.user_profiles:
254
+ self.load_memory(user_id)
255
+
256
+ return self.user_profiles.get(user_id)
257
+