Conversation Manager API¶
Overview¶
The ConversationManager
class provides robust conversation persistence with atomic operations, validation, backup mechanisms, and comprehensive error handling for the Convoscope application.
Core Functionality¶
save_conversation¶
Primary method for persisting conversations with comprehensive data integrity protection.
Parameters:
- conversation
(List[Dict[str, str]]): List of conversation messages in standard format
- filename
(str): Target filename for conversation storage
- create_backup
(bool, optional): Whether to create backup before writing (default: True)
Returns:
- Tuple[bool, str]
: Success status and descriptive message
Features: - Atomic Operations: Uses temporary files and atomic moves to prevent corruption - Backup Protection: Automatically creates backup before modifying existing conversations - Input Validation: Comprehensive validation of conversation structure and content - Error Recovery: Automatic rollback to backup on write failures - Filename Sanitization: Prevents directory traversal and ensures valid filenames
Example:
manager = ConversationManager()
conversation = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing well, thank you!"}
]
success, message = manager.save_conversation(
conversation=conversation,
filename="my_chat_session",
create_backup=True
)
if success:
print(f"✅ {message}")
else:
print(f"❌ Save failed: {message}")
load_conversation¶
Safely loads and validates conversation files with error handling.
Parameters:
- filename
(str): Name of conversation file to load (without .json extension)
Returns:
- Tuple[bool, Union[List[Dict], str]]
: Success status and either conversation data or error message
Validation Features: - File existence checking - JSON format validation - Conversation structure verification - Message format validation - Encoding error handling
Example:
# Load existing conversation
success, result = manager.load_conversation("my_chat_session")
if success:
conversation = result
print(f"Loaded {len(conversation)} messages")
for message in conversation:
print(f"{message['role']}: {message['content'][:50]}...")
else:
error_message = result
print(f"Load failed: {error_message}")
get_conversation_list¶
Retrieves list of available conversation files with metadata.
Parameters:
- include_metadata
(bool, optional): Whether to include file metadata (default: False)
Returns:
- List[Union[str, Dict]]
: List of filenames or metadata dictionaries
Metadata Fields (when include_metadata=True
):
- filename
: Base filename without extension
- full_path
: Complete file path
- size_bytes
: File size in bytes
- modified_time
: Last modification timestamp
- message_count
: Number of messages (if parseable)
Example:
# Simple filename list
conversations = manager.get_conversation_list()
print(f"Available conversations: {conversations}")
# Detailed metadata
detailed_list = manager.get_conversation_list(include_metadata=True)
for conv in detailed_list:
print(f"{conv['filename']}: {conv['message_count']} messages, "
f"modified {conv['modified_time']}")
delete_conversation¶
Safely removes conversation files with backup option.
Parameters:
- filename
(str): Name of conversation to delete
- create_backup
(bool, optional): Create backup before deletion (default: True)
Returns:
- Tuple[bool, str]
: Success status and descriptive message
Safety Features: - Backup creation before deletion - File existence verification - Permission checking - Recovery instructions on failure
Example:
# Delete with backup (recommended)
success, message = manager.delete_conversation("old_session", create_backup=True)
# Force delete without backup (use carefully)
success, message = manager.delete_conversation("temp_session", create_backup=False)
Validation Methods¶
validate_conversation¶
Comprehensive conversation structure and content validation.
Parameters:
- conversation
(List[Dict]): Conversation data to validate
Returns:
- bool
: True if valid, False otherwise
Validation Rules: - Must be a non-empty list - Each message must be a dictionary - Required keys: 'role' and 'content' - Role must be: 'system', 'user', or 'assistant' - Content must be non-empty string - No malicious content patterns
Example:
# Valid conversation
valid_conversation = [
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hi there!"}
]
# Invalid examples
invalid_examples = [
[], # Empty list
[{"role": "invalid", "content": "test"}], # Invalid role
[{"role": "user", "content": ""}], # Empty content
[{"missing_role": "content"}], # Missing required keys
]
assert manager.validate_conversation(valid_conversation) == True
for invalid in invalid_examples:
assert manager.validate_conversation(invalid) == False
validate_filename¶
Ensures filename safety and prevents security issues.
Parameters:
- filename
(str): Filename to validate and sanitize
Returns:
- str
: Sanitized filename safe for filesystem use
Sanitization Rules:
- Removes directory traversal patterns (../
, ./
)
- Strips dangerous characters (<
, >
, |
, etc.)
- Limits length to reasonable bounds
- Ensures non-empty result
- Adds fallback names for invalid inputs
Example:
# Safe filename remains unchanged
safe_name = manager.validate_filename("my_conversation")
assert safe_name == "my_conversation"
# Dangerous filename gets sanitized
dangerous = "../../../etc/passwd"
sanitized = manager.validate_filename(dangerous)
assert sanitized == "etc_passwd" # Directory traversal removed
# Special characters handled
special = "conv<>|?*eration"
clean = manager.validate_filename(special)
assert clean == "conv_eration" # Special chars replaced with underscores
Error Handling¶
ConversationManagerError¶
Base exception class for conversation management errors.
Common Error Types: - FileNotFoundError: Conversation file doesn't exist - PermissionError: Insufficient filesystem permissions - ValidationError: Invalid conversation data format - CorruptionError: File corruption detected - BackupError: Backup operation failed
Error Handling Patterns:
from src.services.conversation_manager import ConversationManagerError
try:
success, result = manager.load_conversation("nonexistent")
if not success:
print(f"Load failed: {result}")
except ConversationManagerError as e:
error_type = type(e).__name__
if "FileNotFound" in error_type:
print("File doesn't exist - create new conversation?")
elif "Permission" in error_type:
print("Check file permissions or run with appropriate access")
elif "Validation" in error_type:
print("Conversation data is corrupted or invalid format")
else:
print(f"Unexpected error: {e}")
Configuration & Setup¶
Directory Management¶
Automatic Directory Creation:
# Default conversation directory
manager = ConversationManager() # Uses './conversation_history/'
# Custom directory
manager = ConversationManager(base_dir="/custom/path/conversations")
# Directory created automatically if it doesn't exist
Directory Structure:
conversation_history/
├── conversation_1.json
├── conversation_2.json
├── backups/
│ ├── conversation_1.backup
│ └── conversation_2.backup
└── .gitkeep
File Format Specification¶
Conversations are stored as JSON files with standardized structure:
[
{
"role": "system",
"content": "You are a helpful assistant.",
"timestamp": "2025-01-07T10:30:00Z"
},
{
"role": "user",
"content": "What is the weather like today?",
"timestamp": "2025-01-07T10:30:15Z"
},
{
"role": "assistant",
"content": "I don't have access to current weather data, but I can help you find weather information.",
"timestamp": "2025-01-07T10:30:18Z"
}
]
Advanced Usage Patterns¶
Batch Operations¶
def backup_all_conversations(manager):
"""Create backups of all conversations."""
conversations = manager.get_conversation_list()
for filename in conversations:
success, conversation = manager.load_conversation(filename)
if success:
backup_name = f"{filename}_backup_{datetime.now().strftime('%Y%m%d')}"
manager.save_conversation(conversation, backup_name, create_backup=False)
Conversation Migration¶
def migrate_conversation_format(manager, filename):
"""Migrate old format conversations to new format."""
success, conversation = manager.load_conversation(filename)
if success:
# Add timestamps to messages missing them
for message in conversation:
if 'timestamp' not in message:
message['timestamp'] = datetime.now().isoformat()
# Save with updated format
return manager.save_conversation(conversation, filename)
return False, "Failed to load conversation for migration"
Conversation Analysis¶
def analyze_conversation(manager, filename):
"""Analyze conversation statistics."""
success, conversation = manager.load_conversation(filename)
if not success:
return None
stats = {
'total_messages': len(conversation),
'user_messages': len([m for m in conversation if m['role'] == 'user']),
'assistant_messages': len([m for m in conversation if m['role'] == 'assistant']),
'total_characters': sum(len(m['content']) for m in conversation),
'average_message_length': sum(len(m['content']) for m in conversation) / len(conversation)
}
return stats
Integration Examples¶
Streamlit Integration¶
import streamlit as st
from src.services.conversation_manager import ConversationManager
@st.cache_resource
def get_conversation_manager():
"""Cached conversation manager instance."""
return ConversationManager()
def save_current_conversation():
"""Save current Streamlit session conversation."""
if 'conversation' in st.session_state:
manager = get_conversation_manager()
filename = st.text_input("Save as:", value="my_conversation")
if st.button("Save"):
success, message = manager.save_conversation(
st.session_state.conversation,
filename
)
if success:
st.success(message)
else:
st.error(f"Save failed: {message}")
def load_conversation_selector():
"""Conversation loading interface."""
manager = get_conversation_manager()
conversations = manager.get_conversation_list()
if conversations:
selected = st.selectbox("Load conversation:", conversations)
if st.button("Load"):
success, result = manager.load_conversation(selected)
if success:
st.session_state.conversation = result
st.rerun()
else:
st.error(f"Load failed: {result}")
else:
st.info("No saved conversations found")
Background Auto-Save¶
import threading
import time
from queue import Queue
class AutoSaveManager:
"""Background auto-save for conversations."""
def __init__(self, conversation_manager, save_interval=30):
self.manager = conversation_manager
self.save_queue = Queue()
self.save_interval = save_interval
self.running = True
# Start background thread
self.thread = threading.Thread(target=self._auto_save_worker)
self.thread.daemon = True
self.thread.start()
def queue_save(self, conversation, filename):
"""Queue conversation for auto-save."""
self.save_queue.put((conversation.copy(), filename))
def _auto_save_worker(self):
"""Background worker for auto-saving."""
while self.running:
if not self.save_queue.empty():
conversation, filename = self.save_queue.get()
try:
success, message = self.manager.save_conversation(
conversation,
f"autosave_{filename}",
create_backup=False
)
if not success:
print(f"Auto-save failed: {message}")
except Exception as e:
print(f"Auto-save error: {e}")
time.sleep(self.save_interval)
# Usage
auto_saver = AutoSaveManager(conversation_manager, save_interval=60)
auto_saver.queue_save(current_conversation, "session_1")
This API provides comprehensive conversation management capabilities with enterprise-grade reliability and data protection features.
Next: Utilities API - Helper functions and session state management