from gtts import gTTS import io import gradio as gr from datetime import datetime, timedelta import json class MemoryHandler: def __init__(self): self.conversation_history = [] self.max_history = 5 # Keep last 5 interactions self.context_timeout = timedelta(minutes=2) # Context expires after 2 minutes self.last_interaction_time = None self.partial_info = { 'project_number': None, 'project_name': None, 'amount': None, 'reason': None, 'timestamp': None } self.confidence_scores = { 'project_number': 0.0, 'project_name': 0.0, 'amount': 0.0, 'reason': 0.0 } def add_interaction(self, text: str, extracted_info: dict = None) -> None: """ Add a new interaction to the conversation history and update partial information Args: text: The text from the voice/text input extracted_info: Dictionary containing extracted request details """ current_time = datetime.now() # Check if we should clear context due to timeout if self.last_interaction_time and \ (current_time - self.last_interaction_time) > self.context_timeout: self.clear_partial_info() # Update conversation history if text: # Add timestamp to conversation history self.conversation_history.append({ 'text': text, 'timestamp': current_time.isoformat(), 'extracted_info': extracted_info }) if len(self.conversation_history) > self.max_history: self.conversation_history.pop(0) # Update partial information if provided if extracted_info: self._update_partial_info(extracted_info, current_time) self.last_interaction_time = current_time def _update_partial_info(self, extracted_info: dict, current_time: datetime) -> None: """ Update partial information with confidence scoring """ for key in self.partial_info: if key in extracted_info and extracted_info[key]: new_value = extracted_info[key] current_value = self.partial_info[key] # Update if empty or higher confidence if (current_value is None or extracted_info.get(f'{key}_confidence', 0.5) > self.confidence_scores.get(key, 0)): self.partial_info[key] = new_value self.confidence_scores[key] = extracted_info.get(f'{key}_confidence', 0.5) self.partial_info['timestamp'] = current_time def get_context(self) -> str: """ Get the current conversation context including partial information """ # Start with the most recent conversation history context_parts = [] # Add conversation history with timestamps for entry in self.conversation_history: timestamp = datetime.fromisoformat(entry['timestamp']).strftime('%H:%M:%S') context_parts.append(f"[{timestamp}] {entry['text']}") context = " ".join(context_parts) # Add partial information to context if available partial_context = [] for key, value in self.partial_info.items(): if value and key != 'timestamp': confidence = self.confidence_scores.get(key, 0) partial_context.append(f"{key}: {value} (confidence: {confidence:.2f})") if partial_context: context += "\nPartial information: " + ", ".join(partial_context) return context def get_partial_info(self) -> dict: """Get current partial information with confidence scores""" info = {k: v for k, v in self.partial_info.items() if k != 'timestamp' and v is not None} info['confidence_scores'] = self.confidence_scores return info def merge_partial_info(self, new_info: dict) -> None: """ Merge new information with existing partial info based on confidence scores """ for key in self.partial_info: if key in new_info and new_info[key] is not None: new_confidence = new_info.get(f'{key}_confidence', 0.5) if (self.partial_info[key] is None or new_confidence > self.confidence_scores.get(key, 0)): self.partial_info[key] = new_info[key] self.confidence_scores[key] = new_confidence def clear_partial_info(self) -> None: """Clear partial information and confidence scores""" self.partial_info = { 'project_number': None, 'project_name': None, 'amount': None, 'reason': None, 'timestamp': None } self.confidence_scores = { 'project_number': 0.0, 'project_name': 0.0, 'amount': 0.0, 'reason': 0.0 } def clear_memory(self) -> None: """Clear all conversation history and partial information""" self.conversation_history = [] self.clear_partial_info() self.last_interaction_time = None return "Memory cleared!" def get_missing_fields(self) -> list: """Get list of missing required fields with confidence thresholds""" missing = [] confidence_threshold = 0.5 # Minimum confidence required for field in ['project_number', 'project_name', 'amount', 'reason']: if (self.partial_info.get(field) is None or self.confidence_scores.get(field, 0) < confidence_threshold): missing.append(field) return missing def text_to_speech(self, text: str) -> tuple[str, str]: """Convert text to speech and return audio path""" try: tts = gTTS(text=text, lang='en') audio_path = "temp_audio.mp3" tts.save(audio_path) return audio_path, None except Exception as e: return None, f"Error generating audio: {str(e)}" def create_confirmation_audio(self, project_number: str, project_name: str, amount: float, reason: str) -> tuple[str, str]: """Create confirmation message audio with confidence information""" confidence_info = "\n".join([ f"{field}: {self.confidence_scores.get(field, 0):.2f} confidence" for field in ['project_number', 'project_name', 'amount', 'reason'] ]) confirmation_text = ( f"You are going to add request money for project ID: {project_number}, " f"Project name: {project_name}, request amount: {amount}, " f"reason: {reason}.\n\nConfidence scores:\n{confidence_info}\n" f"Are you sure you want to proceed?" ) return self.text_to_speech(confirmation_text) def get_prompt_for_missing_info(self) -> str: """Generate a prompt for missing information with confidence scores""" missing = self.get_missing_fields() if not missing: return "All required information has been provided with sufficient confidence." current_info = self.get_partial_info() prompt = "Current information:\n" # Show current information with confidence scores for key, value in current_info.items(): if key != 'confidence_scores' and value is not None: confidence = self.confidence_scores.get(key, 0) prompt += f"- {key}: {value} (confidence: {confidence:.2f})\n" prompt += "\nPlease provide or clarify the following information:\n" for field in missing: current_confidence = self.confidence_scores.get(field, 0) if current_confidence > 0: prompt += f"- {field} (current confidence: {current_confidence:.2f}, needs improvement)\n" else: prompt += f"- {field} (missing)\n" return prompt def to_json(self) -> str: """Serialize the memory state to JSON""" return json.dumps({ 'conversation_history': self.conversation_history, 'partial_info': self.partial_info, 'confidence_scores': self.confidence_scores, 'last_interaction_time': self.last_interaction_time.isoformat() if self.last_interaction_time else None }) def from_json(self, json_str: str) -> None: """Restore memory state from JSON""" data = json.loads(json_str) self.conversation_history = data['conversation_history'] self.partial_info = data['partial_info'] self.confidence_scores = data['confidence_scores'] self.last_interaction_time = (datetime.fromisoformat(data['last_interaction_time']) if data['last_interaction_time'] else None)