mem-llm 2.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.
- mem_llm/__init__.py +98 -0
- mem_llm/api_server.py +595 -0
- mem_llm/base_llm_client.py +201 -0
- mem_llm/builtin_tools.py +311 -0
- mem_llm/cli.py +254 -0
- mem_llm/clients/__init__.py +22 -0
- mem_llm/clients/lmstudio_client.py +393 -0
- mem_llm/clients/ollama_client.py +354 -0
- mem_llm/config.yaml.example +52 -0
- mem_llm/config_from_docs.py +180 -0
- mem_llm/config_manager.py +231 -0
- mem_llm/conversation_summarizer.py +372 -0
- mem_llm/data_export_import.py +640 -0
- mem_llm/dynamic_prompt.py +298 -0
- mem_llm/knowledge_loader.py +88 -0
- mem_llm/llm_client.py +225 -0
- mem_llm/llm_client_factory.py +260 -0
- mem_llm/logger.py +129 -0
- mem_llm/mem_agent.py +1611 -0
- mem_llm/memory_db.py +612 -0
- mem_llm/memory_manager.py +321 -0
- mem_llm/memory_tools.py +253 -0
- mem_llm/prompt_security.py +304 -0
- mem_llm/response_metrics.py +221 -0
- mem_llm/retry_handler.py +193 -0
- mem_llm/thread_safe_db.py +301 -0
- mem_llm/tool_system.py +429 -0
- mem_llm/vector_store.py +278 -0
- mem_llm/web_launcher.py +129 -0
- mem_llm/web_ui/README.md +44 -0
- mem_llm/web_ui/__init__.py +7 -0
- mem_llm/web_ui/index.html +641 -0
- mem_llm/web_ui/memory.html +569 -0
- mem_llm/web_ui/metrics.html +75 -0
- mem_llm-2.0.0.dist-info/METADATA +667 -0
- mem_llm-2.0.0.dist-info/RECORD +39 -0
- mem_llm-2.0.0.dist-info/WHEEL +5 -0
- mem_llm-2.0.0.dist-info/entry_points.txt +3 -0
- mem_llm-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data Export/Import System
|
|
3
|
+
Supports multiple formats and databases: JSON, CSV, SQLite, PostgreSQL, MongoDB
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import csv
|
|
8
|
+
import sqlite3
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Dict, List, Optional, Any, Union
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DataExporter:
|
|
18
|
+
"""Export memory data to various formats and databases"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, memory_manager):
|
|
21
|
+
"""
|
|
22
|
+
Args:
|
|
23
|
+
memory_manager: MemoryManager or SQLMemoryManager instance
|
|
24
|
+
"""
|
|
25
|
+
self.memory = memory_manager
|
|
26
|
+
|
|
27
|
+
def export_to_json(self, user_id: str, output_file: str) -> Dict[str, Any]:
|
|
28
|
+
"""
|
|
29
|
+
Export user data to JSON file
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
user_id: User ID to export
|
|
33
|
+
output_file: Output JSON file path
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Export statistics
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
# Get all user data
|
|
40
|
+
conversations = self.memory.get_recent_conversations(user_id, limit=1000)
|
|
41
|
+
profile = getattr(self.memory, 'user_profiles', {}).get(user_id, {})
|
|
42
|
+
|
|
43
|
+
data = {
|
|
44
|
+
'user_id': user_id,
|
|
45
|
+
'export_date': datetime.now().isoformat(),
|
|
46
|
+
'conversations': conversations,
|
|
47
|
+
'profile': profile,
|
|
48
|
+
'metadata': {
|
|
49
|
+
'total_conversations': len(conversations),
|
|
50
|
+
'format': 'json',
|
|
51
|
+
'version': '1.0'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Write to file
|
|
56
|
+
output_path = Path(output_file)
|
|
57
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
|
|
59
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
60
|
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
61
|
+
|
|
62
|
+
logger.info(f"Exported {len(conversations)} conversations to {output_file}")
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
'success': True,
|
|
66
|
+
'file': str(output_path),
|
|
67
|
+
'conversations': len(conversations),
|
|
68
|
+
'size_bytes': output_path.stat().st_size
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"JSON export failed: {e}")
|
|
73
|
+
return {'success': False, 'error': str(e)}
|
|
74
|
+
|
|
75
|
+
def export_to_csv(self, user_id: str, output_file: str) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Export conversations to CSV file
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
user_id: User ID to export
|
|
81
|
+
output_file: Output CSV file path
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Export statistics
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
conversations = self.memory.get_recent_conversations(user_id, limit=1000)
|
|
88
|
+
|
|
89
|
+
output_path = Path(output_file)
|
|
90
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
with open(output_path, 'w', newline='', encoding='utf-8') as f:
|
|
93
|
+
writer = csv.DictWriter(f, fieldnames=[
|
|
94
|
+
'timestamp', 'user_message', 'bot_response', 'metadata'
|
|
95
|
+
])
|
|
96
|
+
writer.writeheader()
|
|
97
|
+
|
|
98
|
+
for conv in conversations:
|
|
99
|
+
writer.writerow({
|
|
100
|
+
'timestamp': conv.get('timestamp', ''),
|
|
101
|
+
'user_message': conv.get('user_message', ''),
|
|
102
|
+
'bot_response': conv.get('bot_response', ''),
|
|
103
|
+
'metadata': json.dumps(conv.get('metadata', {}))
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
logger.info(f"Exported {len(conversations)} conversations to CSV: {output_file}")
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
'success': True,
|
|
110
|
+
'file': str(output_path),
|
|
111
|
+
'conversations': len(conversations),
|
|
112
|
+
'size_bytes': output_path.stat().st_size
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"CSV export failed: {e}")
|
|
117
|
+
return {'success': False, 'error': str(e)}
|
|
118
|
+
|
|
119
|
+
def export_to_sqlite(self, user_id: str, db_file: str) -> Dict[str, Any]:
|
|
120
|
+
"""
|
|
121
|
+
Export to SQLite database
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
user_id: User ID to export
|
|
125
|
+
db_file: SQLite database file path
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Export statistics
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
conversations = self.memory.get_recent_conversations(user_id, limit=1000)
|
|
132
|
+
|
|
133
|
+
db_path = Path(db_file)
|
|
134
|
+
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
135
|
+
|
|
136
|
+
conn = sqlite3.connect(str(db_path))
|
|
137
|
+
cursor = conn.cursor()
|
|
138
|
+
|
|
139
|
+
# Create table
|
|
140
|
+
cursor.execute('''
|
|
141
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
142
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
143
|
+
user_id TEXT NOT NULL,
|
|
144
|
+
timestamp TEXT NOT NULL,
|
|
145
|
+
user_message TEXT NOT NULL,
|
|
146
|
+
bot_response TEXT NOT NULL,
|
|
147
|
+
metadata TEXT
|
|
148
|
+
)
|
|
149
|
+
''')
|
|
150
|
+
|
|
151
|
+
# Insert conversations
|
|
152
|
+
for conv in conversations:
|
|
153
|
+
cursor.execute('''
|
|
154
|
+
INSERT INTO conversations
|
|
155
|
+
(user_id, timestamp, user_message, bot_response, metadata)
|
|
156
|
+
VALUES (?, ?, ?, ?, ?)
|
|
157
|
+
''', (
|
|
158
|
+
user_id,
|
|
159
|
+
conv.get('timestamp', datetime.now().isoformat()),
|
|
160
|
+
conv.get('user_message', ''),
|
|
161
|
+
conv.get('bot_response', ''),
|
|
162
|
+
json.dumps(conv.get('metadata', {}))
|
|
163
|
+
))
|
|
164
|
+
|
|
165
|
+
conn.commit()
|
|
166
|
+
conn.close()
|
|
167
|
+
|
|
168
|
+
logger.info(f"Exported {len(conversations)} conversations to SQLite: {db_file}")
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
'success': True,
|
|
172
|
+
'file': str(db_path),
|
|
173
|
+
'conversations': len(conversations),
|
|
174
|
+
'size_bytes': db_path.stat().st_size
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.error(f"SQLite export failed: {e}")
|
|
179
|
+
return {'success': False, 'error': str(e)}
|
|
180
|
+
|
|
181
|
+
def export_to_postgresql(self, user_id: str, connection_string: str) -> Dict[str, Any]:
|
|
182
|
+
"""
|
|
183
|
+
Export to PostgreSQL database
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
user_id: User ID to export
|
|
187
|
+
connection_string: PostgreSQL connection string
|
|
188
|
+
(e.g., "postgresql://user:pass@localhost/dbname")
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Export statistics
|
|
192
|
+
"""
|
|
193
|
+
try:
|
|
194
|
+
import psycopg2
|
|
195
|
+
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
|
|
196
|
+
except ImportError:
|
|
197
|
+
return {
|
|
198
|
+
'success': False,
|
|
199
|
+
'error': 'psycopg2 not installed. Install: pip install psycopg2-binary'
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
conversations = self.memory.get_recent_conversations(user_id, limit=1000)
|
|
204
|
+
|
|
205
|
+
# Parse connection string to get database name
|
|
206
|
+
import re
|
|
207
|
+
match = re.search(r'/([^/]+)(?:\?|$)', connection_string)
|
|
208
|
+
db_name = match.group(1) if match else None
|
|
209
|
+
|
|
210
|
+
# Try to connect, if database doesn't exist, create it
|
|
211
|
+
try:
|
|
212
|
+
conn = psycopg2.connect(connection_string)
|
|
213
|
+
except psycopg2.OperationalError as e:
|
|
214
|
+
if "does not exist" in str(e) and db_name:
|
|
215
|
+
# Connect to default 'postgres' database to create new one
|
|
216
|
+
base_conn_string = connection_string.rsplit('/', 1)[0] + '/postgres'
|
|
217
|
+
temp_conn = psycopg2.connect(base_conn_string)
|
|
218
|
+
temp_conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
|
219
|
+
temp_cursor = temp_conn.cursor()
|
|
220
|
+
temp_cursor.execute(f'CREATE DATABASE {db_name}')
|
|
221
|
+
temp_cursor.close()
|
|
222
|
+
temp_conn.close()
|
|
223
|
+
logger.info(f"Created PostgreSQL database: {db_name}")
|
|
224
|
+
|
|
225
|
+
# Now connect to the new database
|
|
226
|
+
conn = psycopg2.connect(connection_string)
|
|
227
|
+
else:
|
|
228
|
+
raise
|
|
229
|
+
|
|
230
|
+
cursor = conn.cursor()
|
|
231
|
+
|
|
232
|
+
# Create table if not exists
|
|
233
|
+
cursor.execute('''
|
|
234
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
235
|
+
id SERIAL PRIMARY KEY,
|
|
236
|
+
user_id VARCHAR(255) NOT NULL,
|
|
237
|
+
timestamp TIMESTAMP NOT NULL,
|
|
238
|
+
user_message TEXT NOT NULL,
|
|
239
|
+
bot_response TEXT NOT NULL,
|
|
240
|
+
metadata JSONB
|
|
241
|
+
)
|
|
242
|
+
''')
|
|
243
|
+
|
|
244
|
+
# Insert conversations
|
|
245
|
+
for conv in conversations:
|
|
246
|
+
cursor.execute('''
|
|
247
|
+
INSERT INTO conversations
|
|
248
|
+
(user_id, timestamp, user_message, bot_response, metadata)
|
|
249
|
+
VALUES (%s, %s, %s, %s, %s)
|
|
250
|
+
''', (
|
|
251
|
+
user_id,
|
|
252
|
+
conv.get('timestamp', datetime.now().isoformat()),
|
|
253
|
+
conv.get('user_message', ''),
|
|
254
|
+
conv.get('bot_response', ''),
|
|
255
|
+
json.dumps(conv.get('metadata', {}))
|
|
256
|
+
))
|
|
257
|
+
|
|
258
|
+
conn.commit()
|
|
259
|
+
cursor.close()
|
|
260
|
+
conn.close()
|
|
261
|
+
|
|
262
|
+
logger.info(f"Exported {len(conversations)} conversations to PostgreSQL")
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
'success': True,
|
|
266
|
+
'database': 'postgresql',
|
|
267
|
+
'conversations': len(conversations),
|
|
268
|
+
'database_created': db_name is not None
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logger.error(f"PostgreSQL export failed: {e}")
|
|
273
|
+
return {'success': False, 'error': str(e)}
|
|
274
|
+
|
|
275
|
+
def export_to_mongodb(self, user_id: str, connection_string: str,
|
|
276
|
+
database: str = 'mem_llm', collection: str = 'conversations') -> Dict[str, Any]:
|
|
277
|
+
"""
|
|
278
|
+
Export to MongoDB database
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
user_id: User ID to export
|
|
282
|
+
connection_string: MongoDB connection string
|
|
283
|
+
(e.g., "mongodb://localhost:27017/")
|
|
284
|
+
database: Database name
|
|
285
|
+
collection: Collection name
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Export statistics
|
|
289
|
+
"""
|
|
290
|
+
try:
|
|
291
|
+
from pymongo import MongoClient
|
|
292
|
+
except ImportError:
|
|
293
|
+
return {
|
|
294
|
+
'success': False,
|
|
295
|
+
'error': 'pymongo not installed. Install: pip install pymongo'
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
conversations = self.memory.get_recent_conversations(user_id, limit=1000)
|
|
300
|
+
|
|
301
|
+
client = MongoClient(connection_string)
|
|
302
|
+
|
|
303
|
+
# MongoDB automatically creates database and collection if they don't exist
|
|
304
|
+
db = client[database]
|
|
305
|
+
coll = db[collection]
|
|
306
|
+
|
|
307
|
+
# Check if this is a new database/collection
|
|
308
|
+
is_new_db = database not in client.list_database_names()
|
|
309
|
+
is_new_collection = collection not in db.list_collection_names()
|
|
310
|
+
|
|
311
|
+
if is_new_db:
|
|
312
|
+
logger.info(f"Creating MongoDB database: {database}")
|
|
313
|
+
if is_new_collection:
|
|
314
|
+
logger.info(f"Creating MongoDB collection: {collection}")
|
|
315
|
+
|
|
316
|
+
# Prepare documents
|
|
317
|
+
documents = []
|
|
318
|
+
for conv in conversations:
|
|
319
|
+
doc = {
|
|
320
|
+
'user_id': user_id,
|
|
321
|
+
'timestamp': conv.get('timestamp', datetime.now().isoformat()),
|
|
322
|
+
'user_message': conv.get('user_message', ''),
|
|
323
|
+
'bot_response': conv.get('bot_response', ''),
|
|
324
|
+
'metadata': conv.get('metadata', {}),
|
|
325
|
+
'export_date': datetime.now()
|
|
326
|
+
}
|
|
327
|
+
documents.append(doc)
|
|
328
|
+
|
|
329
|
+
# Insert documents
|
|
330
|
+
result = coll.insert_many(documents)
|
|
331
|
+
|
|
332
|
+
client.close()
|
|
333
|
+
|
|
334
|
+
logger.info(f"Exported {len(conversations)} conversations to MongoDB")
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
'success': True,
|
|
338
|
+
'database': 'mongodb',
|
|
339
|
+
'conversations': len(conversations),
|
|
340
|
+
'inserted_ids': len(result.inserted_ids),
|
|
341
|
+
'database_created': is_new_db,
|
|
342
|
+
'collection_created': is_new_collection
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
except Exception as e:
|
|
346
|
+
logger.error(f"MongoDB export failed: {e}")
|
|
347
|
+
return {'success': False, 'error': str(e)}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class DataImporter:
|
|
351
|
+
"""Import memory data from various formats and databases"""
|
|
352
|
+
|
|
353
|
+
def __init__(self, memory_manager):
|
|
354
|
+
"""
|
|
355
|
+
Args:
|
|
356
|
+
memory_manager: MemoryManager or SQLMemoryManager instance
|
|
357
|
+
"""
|
|
358
|
+
self.memory = memory_manager
|
|
359
|
+
|
|
360
|
+
def import_from_json(self, input_file: str, user_id: Optional[str] = None) -> Dict[str, Any]:
|
|
361
|
+
"""
|
|
362
|
+
Import user data from JSON file
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
input_file: Input JSON file path
|
|
366
|
+
user_id: Override user ID (use file's user_id if None)
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Import statistics
|
|
370
|
+
"""
|
|
371
|
+
try:
|
|
372
|
+
input_path = Path(input_file)
|
|
373
|
+
|
|
374
|
+
if not input_path.exists():
|
|
375
|
+
return {'success': False, 'error': f'File not found: {input_file}'}
|
|
376
|
+
|
|
377
|
+
with open(input_path, 'r', encoding='utf-8') as f:
|
|
378
|
+
data = json.load(f)
|
|
379
|
+
|
|
380
|
+
# Get user ID
|
|
381
|
+
target_user_id = user_id or data.get('user_id')
|
|
382
|
+
if not target_user_id:
|
|
383
|
+
return {'success': False, 'error': 'No user_id specified'}
|
|
384
|
+
|
|
385
|
+
# Import conversations
|
|
386
|
+
conversations = data.get('conversations', [])
|
|
387
|
+
imported = 0
|
|
388
|
+
|
|
389
|
+
for conv in conversations:
|
|
390
|
+
self.memory.add_conversation(
|
|
391
|
+
target_user_id,
|
|
392
|
+
conv.get('user_message', ''),
|
|
393
|
+
conv.get('bot_response', ''),
|
|
394
|
+
conv.get('metadata', {})
|
|
395
|
+
)
|
|
396
|
+
imported += 1
|
|
397
|
+
|
|
398
|
+
# Import profile if available
|
|
399
|
+
if 'profile' in data and hasattr(self.memory, 'update_profile'):
|
|
400
|
+
self.memory.update_profile(target_user_id, data['profile'])
|
|
401
|
+
|
|
402
|
+
logger.info(f"Imported {imported} conversations from {input_file}")
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
'success': True,
|
|
406
|
+
'file': str(input_path),
|
|
407
|
+
'user_id': target_user_id,
|
|
408
|
+
'conversations': imported
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
except Exception as e:
|
|
412
|
+
logger.error(f"JSON import failed: {e}")
|
|
413
|
+
return {'success': False, 'error': str(e)}
|
|
414
|
+
|
|
415
|
+
def import_from_csv(self, input_file: str, user_id: str) -> Dict[str, Any]:
|
|
416
|
+
"""
|
|
417
|
+
Import conversations from CSV file
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
input_file: Input CSV file path
|
|
421
|
+
user_id: User ID for imported conversations
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
Import statistics
|
|
425
|
+
"""
|
|
426
|
+
try:
|
|
427
|
+
input_path = Path(input_file)
|
|
428
|
+
|
|
429
|
+
if not input_path.exists():
|
|
430
|
+
return {'success': False, 'error': f'File not found: {input_file}'}
|
|
431
|
+
|
|
432
|
+
imported = 0
|
|
433
|
+
|
|
434
|
+
with open(input_path, 'r', encoding='utf-8') as f:
|
|
435
|
+
reader = csv.DictReader(f)
|
|
436
|
+
|
|
437
|
+
for row in reader:
|
|
438
|
+
metadata = {}
|
|
439
|
+
if row.get('metadata'):
|
|
440
|
+
try:
|
|
441
|
+
metadata = json.loads(row['metadata'])
|
|
442
|
+
except:
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
self.memory.add_conversation(
|
|
446
|
+
user_id,
|
|
447
|
+
row.get('user_message', ''),
|
|
448
|
+
row.get('bot_response', ''),
|
|
449
|
+
metadata
|
|
450
|
+
)
|
|
451
|
+
imported += 1
|
|
452
|
+
|
|
453
|
+
logger.info(f"Imported {imported} conversations from CSV: {input_file}")
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
'success': True,
|
|
457
|
+
'file': str(input_path),
|
|
458
|
+
'user_id': user_id,
|
|
459
|
+
'conversations': imported
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
except Exception as e:
|
|
463
|
+
logger.error(f"CSV import failed: {e}")
|
|
464
|
+
return {'success': False, 'error': str(e)}
|
|
465
|
+
|
|
466
|
+
def import_from_sqlite(self, db_file: str, user_id: str) -> Dict[str, Any]:
|
|
467
|
+
"""
|
|
468
|
+
Import from SQLite database
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
db_file: SQLite database file path
|
|
472
|
+
user_id: User ID to import data for
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
Import statistics
|
|
476
|
+
"""
|
|
477
|
+
try:
|
|
478
|
+
db_path = Path(db_file)
|
|
479
|
+
|
|
480
|
+
if not db_path.exists():
|
|
481
|
+
return {'success': False, 'error': f'Database not found: {db_file}'}
|
|
482
|
+
|
|
483
|
+
conn = sqlite3.connect(str(db_path))
|
|
484
|
+
conn.row_factory = sqlite3.Row
|
|
485
|
+
cursor = conn.cursor()
|
|
486
|
+
|
|
487
|
+
# Query conversations
|
|
488
|
+
cursor.execute('''
|
|
489
|
+
SELECT timestamp, user_message, bot_response, metadata
|
|
490
|
+
FROM conversations
|
|
491
|
+
WHERE user_id = ?
|
|
492
|
+
ORDER BY timestamp
|
|
493
|
+
''', (user_id,))
|
|
494
|
+
|
|
495
|
+
imported = 0
|
|
496
|
+
for row in cursor.fetchall():
|
|
497
|
+
metadata = {}
|
|
498
|
+
if row['metadata']:
|
|
499
|
+
try:
|
|
500
|
+
metadata = json.loads(row['metadata'])
|
|
501
|
+
except:
|
|
502
|
+
pass
|
|
503
|
+
|
|
504
|
+
self.memory.add_conversation(
|
|
505
|
+
user_id,
|
|
506
|
+
row['user_message'],
|
|
507
|
+
row['bot_response'],
|
|
508
|
+
metadata
|
|
509
|
+
)
|
|
510
|
+
imported += 1
|
|
511
|
+
|
|
512
|
+
conn.close()
|
|
513
|
+
|
|
514
|
+
logger.info(f"Imported {imported} conversations from SQLite: {db_file}")
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
'success': True,
|
|
518
|
+
'file': str(db_path),
|
|
519
|
+
'user_id': user_id,
|
|
520
|
+
'conversations': imported
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
except Exception as e:
|
|
524
|
+
logger.error(f"SQLite import failed: {e}")
|
|
525
|
+
return {'success': False, 'error': str(e)}
|
|
526
|
+
|
|
527
|
+
def import_from_postgresql(self, connection_string: str, user_id: str) -> Dict[str, Any]:
|
|
528
|
+
"""
|
|
529
|
+
Import from PostgreSQL database
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
connection_string: PostgreSQL connection string
|
|
533
|
+
user_id: User ID to import data for
|
|
534
|
+
|
|
535
|
+
Returns:
|
|
536
|
+
Import statistics
|
|
537
|
+
"""
|
|
538
|
+
try:
|
|
539
|
+
import psycopg2
|
|
540
|
+
from psycopg2.extras import RealDictCursor
|
|
541
|
+
except ImportError:
|
|
542
|
+
return {
|
|
543
|
+
'success': False,
|
|
544
|
+
'error': 'psycopg2 not installed. Install: pip install psycopg2-binary'
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
try:
|
|
548
|
+
conn = psycopg2.connect(connection_string)
|
|
549
|
+
cursor = conn.cursor(cursor_factory=RealDictCursor)
|
|
550
|
+
|
|
551
|
+
# Query conversations
|
|
552
|
+
cursor.execute('''
|
|
553
|
+
SELECT timestamp, user_message, bot_response, metadata
|
|
554
|
+
FROM conversations
|
|
555
|
+
WHERE user_id = %s
|
|
556
|
+
ORDER BY timestamp
|
|
557
|
+
''', (user_id,))
|
|
558
|
+
|
|
559
|
+
imported = 0
|
|
560
|
+
for row in cursor.fetchall():
|
|
561
|
+
metadata = row['metadata'] if isinstance(row['metadata'], dict) else {}
|
|
562
|
+
|
|
563
|
+
self.memory.add_conversation(
|
|
564
|
+
user_id,
|
|
565
|
+
row['user_message'],
|
|
566
|
+
row['bot_response'],
|
|
567
|
+
metadata
|
|
568
|
+
)
|
|
569
|
+
imported += 1
|
|
570
|
+
|
|
571
|
+
cursor.close()
|
|
572
|
+
conn.close()
|
|
573
|
+
|
|
574
|
+
logger.info(f"Imported {imported} conversations from PostgreSQL")
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
'success': True,
|
|
578
|
+
'database': 'postgresql',
|
|
579
|
+
'user_id': user_id,
|
|
580
|
+
'conversations': imported
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
except Exception as e:
|
|
584
|
+
logger.error(f"PostgreSQL import failed: {e}")
|
|
585
|
+
return {'success': False, 'error': str(e)}
|
|
586
|
+
|
|
587
|
+
def import_from_mongodb(self, connection_string: str, user_id: str,
|
|
588
|
+
database: str = 'mem_llm', collection: str = 'conversations') -> Dict[str, Any]:
|
|
589
|
+
"""
|
|
590
|
+
Import from MongoDB database
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
connection_string: MongoDB connection string
|
|
594
|
+
user_id: User ID to import data for
|
|
595
|
+
database: Database name
|
|
596
|
+
collection: Collection name
|
|
597
|
+
|
|
598
|
+
Returns:
|
|
599
|
+
Import statistics
|
|
600
|
+
"""
|
|
601
|
+
try:
|
|
602
|
+
from pymongo import MongoClient
|
|
603
|
+
except ImportError:
|
|
604
|
+
return {
|
|
605
|
+
'success': False,
|
|
606
|
+
'error': 'pymongo not installed. Install: pip install pymongo'
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
client = MongoClient(connection_string)
|
|
611
|
+
db = client[database]
|
|
612
|
+
coll = db[collection]
|
|
613
|
+
|
|
614
|
+
# Query conversations
|
|
615
|
+
documents = coll.find({'user_id': user_id}).sort('timestamp', 1)
|
|
616
|
+
|
|
617
|
+
imported = 0
|
|
618
|
+
for doc in documents:
|
|
619
|
+
self.memory.add_conversation(
|
|
620
|
+
user_id,
|
|
621
|
+
doc.get('user_message', ''),
|
|
622
|
+
doc.get('bot_response', ''),
|
|
623
|
+
doc.get('metadata', {})
|
|
624
|
+
)
|
|
625
|
+
imported += 1
|
|
626
|
+
|
|
627
|
+
client.close()
|
|
628
|
+
|
|
629
|
+
logger.info(f"Imported {imported} conversations from MongoDB")
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
'success': True,
|
|
633
|
+
'database': 'mongodb',
|
|
634
|
+
'user_id': user_id,
|
|
635
|
+
'conversations': imported
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
except Exception as e:
|
|
639
|
+
logger.error(f"MongoDB import failed: {e}")
|
|
640
|
+
return {'success': False, 'error': str(e)}
|