🔧 (conversation_persistence_service.py): Create a service to persist chat conversations to disk for server restarts
126 lines
No EOL
5.2 KiB
Python
126 lines
No EOL
5.2 KiB
Python
"""
|
|
Conversation Persistence Service
|
|
Simple service to persist chat conversations to disk so they survive server restarts
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from typing import Dict, Any
|
|
from datetime import datetime
|
|
import threading
|
|
|
|
|
|
class ConversationPersistenceService:
|
|
"""Simple service to persist conversations to disk"""
|
|
|
|
def __init__(self, storage_file: str = "conversations.json"):
|
|
self.storage_file = storage_file
|
|
self.lock = threading.Lock()
|
|
self._conversations = self._load_conversations()
|
|
|
|
def _load_conversations(self) -> Dict[str, Dict[str, Any]]:
|
|
"""Load conversations from disk"""
|
|
if os.path.exists(self.storage_file):
|
|
try:
|
|
with open(self.storage_file, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
print(f"Loaded {self._count_total_conversations(data)} conversations from {self.storage_file}")
|
|
return data
|
|
except Exception as e:
|
|
print(f"Error loading conversations from {self.storage_file}: {e}")
|
|
return {}
|
|
return {}
|
|
|
|
def _save_conversations(self):
|
|
"""Save conversations to disk"""
|
|
try:
|
|
with self.lock:
|
|
with open(self.storage_file, 'w', encoding='utf-8') as f:
|
|
json.dump(self._conversations, f, indent=2, ensure_ascii=False, default=str)
|
|
print(f"Saved {self._count_total_conversations(self._conversations)} conversations to {self.storage_file}")
|
|
except Exception as e:
|
|
print(f"Error saving conversations to {self.storage_file}: {e}")
|
|
|
|
def _count_total_conversations(self, data: Dict[str, Any]) -> int:
|
|
"""Count total conversations across all users"""
|
|
total = 0
|
|
for user_conversations in data.values():
|
|
if isinstance(user_conversations, dict):
|
|
total += len(user_conversations)
|
|
return total
|
|
|
|
def get_user_conversations(self, user_id: str) -> Dict[str, Any]:
|
|
"""Get all conversations for a user"""
|
|
if user_id not in self._conversations:
|
|
self._conversations[user_id] = {}
|
|
return self._conversations[user_id]
|
|
|
|
def _serialize_datetime(self, obj: Any) -> Any:
|
|
"""Recursively convert datetime objects to ISO strings for JSON serialization"""
|
|
if isinstance(obj, datetime):
|
|
return obj.isoformat()
|
|
elif isinstance(obj, dict):
|
|
return {key: self._serialize_datetime(value) for key, value in obj.items()}
|
|
elif isinstance(obj, list):
|
|
return [self._serialize_datetime(item) for item in obj]
|
|
else:
|
|
return obj
|
|
|
|
def store_conversation_thread(self, user_id: str, response_id: str, conversation_state: Dict[str, Any]):
|
|
"""Store a conversation thread and persist to disk"""
|
|
if user_id not in self._conversations:
|
|
self._conversations[user_id] = {}
|
|
|
|
# Recursively convert datetime objects to strings for JSON serialization
|
|
serialized_conversation = self._serialize_datetime(conversation_state)
|
|
|
|
self._conversations[user_id][response_id] = serialized_conversation
|
|
|
|
# Save to disk (we could optimize this with batching if needed)
|
|
self._save_conversations()
|
|
|
|
def get_conversation_thread(self, user_id: str, response_id: str) -> Dict[str, Any]:
|
|
"""Get a specific conversation thread"""
|
|
user_conversations = self.get_user_conversations(user_id)
|
|
return user_conversations.get(response_id, {})
|
|
|
|
def delete_conversation_thread(self, user_id: str, response_id: str):
|
|
"""Delete a specific conversation thread"""
|
|
if user_id in self._conversations and response_id in self._conversations[user_id]:
|
|
del self._conversations[user_id][response_id]
|
|
self._save_conversations()
|
|
print(f"Deleted conversation {response_id} for user {user_id}")
|
|
|
|
def clear_user_conversations(self, user_id: str):
|
|
"""Clear all conversations for a user"""
|
|
if user_id in self._conversations:
|
|
del self._conversations[user_id]
|
|
self._save_conversations()
|
|
print(f"Cleared all conversations for user {user_id}")
|
|
|
|
def get_storage_stats(self) -> Dict[str, Any]:
|
|
"""Get statistics about stored conversations"""
|
|
total_users = len(self._conversations)
|
|
total_conversations = self._count_total_conversations(self._conversations)
|
|
|
|
user_stats = {}
|
|
for user_id, conversations in self._conversations.items():
|
|
user_stats[user_id] = {
|
|
'conversation_count': len(conversations),
|
|
'latest_activity': max(
|
|
(conv.get('last_activity', '') for conv in conversations.values()),
|
|
default=''
|
|
)
|
|
}
|
|
|
|
return {
|
|
'total_users': total_users,
|
|
'total_conversations': total_conversations,
|
|
'storage_file': self.storage_file,
|
|
'file_exists': os.path.exists(self.storage_file),
|
|
'user_stats': user_stats
|
|
}
|
|
|
|
|
|
# Global instance
|
|
conversation_persistence = ConversationPersistenceService() |