""" Bot Personality System for Advanced Bot Management. This module provides personality templates, configuration, and behavior management for creating diverse and engaging bot characters. """ import json import os from typing import Dict, List, Optional, Any from enum import Enum from pydantic import BaseModel, Field from shared.logger import logger class PersonalityTrait(str, Enum): """Personality traits that can be assigned to bots.""" FRIENDLY = "friendly" PROFESSIONAL = "professional" HUMOROUS = "humorous" SARCASTIC = "sarcastic" EMPATHETIC = "empathetic" ANALYTICAL = "analytical" CREATIVE = "creative" ASSERTIVE = "assertive" PATIENT = "patient" ENTHUSIASTIC = "enthusiastic" MYSTERIOUS = "mysterious" WISE = "wise" PLAYFUL = "playful" FORMAL = "formal" CASUAL = "casual" class CommunicationStyle(str, Enum): """Communication styles for bot responses.""" CONCISE = "concise" DETAILED = "detailed" CONVERSATIONAL = "conversational" TECHNICAL = "technical" STORYTELLING = "storytelling" QUESTION_BASED = "question_based" SUPPORTIVE = "supportive" DIRECT = "direct" DIPLOMATIC = "diplomatic" class ExpertiseDomain(str, Enum): """Domains of expertise for specialized bots.""" GENERAL = "general" TECHNOLOGY = "technology" SCIENCE = "science" ARTS = "arts" BUSINESS = "business" EDUCATION = "education" HEALTH = "health" ENTERTAINMENT = "entertainment" SPORTS = "sports" COOKING = "cooking" TRAVEL = "travel" FINANCE = "finance" LEGAL = "legal" PSYCHOLOGY = "psychology" PHILOSOPHY = "philosophy" class BotPersonality(BaseModel): """Complete personality configuration for a bot.""" # Identity name: str = "Assistant" description: str = "A helpful AI assistant" backstory: Optional[str] = None # Core personality traits: List[PersonalityTrait] = Field(default_factory=lambda: [PersonalityTrait.FRIENDLY]) communication_style: CommunicationStyle = CommunicationStyle.CONVERSATIONAL expertise_domains: List[ExpertiseDomain] = Field(default_factory=lambda: [ExpertiseDomain.GENERAL]) # Behavior settings response_length_preference: str = "medium" # short, medium, long emoji_usage: bool = True formality_level: float = 0.5 # 0.0 = very casual, 1.0 = very formal humor_level: float = 0.3 # 0.0 = no humor, 1.0 = very humorous empathy_level: float = 0.7 # 0.0 = analytical only, 1.0 = highly empathetic # Language preferences preferred_language: str = "en" technical_complexity: float = 0.5 # 0.0 = simple terms, 1.0 = technical jargon # Conversation patterns greeting_style: str = "warm" # formal, warm, casual, quirky farewell_style: str = "friendly" # formal, friendly, casual, memorable # Custom prompts and examples system_prompt: Optional[str] = None example_responses: List[Dict[str, str]] = Field(default_factory=list) custom_instructions: List[str] = Field(default_factory=list) # Behavioral boundaries topics_to_avoid: List[str] = Field(default_factory=list) preferred_topics: List[str] = Field(default_factory=list) conversation_limits: Dict[str, Any] = Field(default_factory=dict) def generate_system_prompt(self) -> str: """Generate a comprehensive system prompt based on personality.""" if self.system_prompt: return self.system_prompt prompt_parts = [] # Identity prompt_parts.append(f"You are {self.name}, {self.description}") if self.backstory: prompt_parts.append(f"Background: {self.backstory}") # Personality traits if self.traits: traits_str = ", ".join([trait.value for trait in self.traits]) prompt_parts.append(f"Your personality is: {traits_str}") # Communication style style_desc = self._get_style_description() prompt_parts.append(f"Communication style: {style_desc}") # Expertise if self.expertise_domains and self.expertise_domains != [ExpertiseDomain.GENERAL]: domains_str = ", ".join([domain.value for domain in self.expertise_domains]) prompt_parts.append(f"Areas of expertise: {domains_str}") # Behavior guidelines behavior_guidelines = self._generate_behavior_guidelines() if behavior_guidelines: prompt_parts.append(f"Behavior guidelines: {behavior_guidelines}") # Custom instructions if self.custom_instructions: prompt_parts.append("Additional instructions:") prompt_parts.extend([f"- {instruction}" for instruction in self.custom_instructions]) # Topic boundaries if self.topics_to_avoid: avoid_str = ", ".join(self.topics_to_avoid) prompt_parts.append(f"Avoid discussing: {avoid_str}") if self.preferred_topics: prefer_str = ", ".join(self.preferred_topics) prompt_parts.append(f"Preferred discussion topics: {prefer_str}") return "\n\n".join(prompt_parts) def _get_style_description(self) -> str: """Get communication style description.""" style_map = { CommunicationStyle.CONCISE: "Keep responses brief and to the point", CommunicationStyle.DETAILED: "Provide comprehensive, detailed explanations", CommunicationStyle.CONVERSATIONAL: "Use natural, conversational language", CommunicationStyle.TECHNICAL: "Use precise, technical terminology when appropriate", CommunicationStyle.STORYTELLING: "Frame responses as engaging narratives", CommunicationStyle.QUESTION_BASED: "Ask follow-up questions to better understand needs", CommunicationStyle.SUPPORTIVE: "Provide encouraging and supportive responses", CommunicationStyle.DIRECT: "Be straightforward and direct in communication", CommunicationStyle.DIPLOMATIC: "Use diplomatic and tactful language" } return style_map.get(self.communication_style, "Communicate naturally") def _generate_behavior_guidelines(self) -> str: """Generate behavior guidelines based on settings.""" guidelines = [] if self.formality_level > 0.7: guidelines.append("maintain a formal and professional tone") elif self.formality_level < 0.3: guidelines.append("use casual and relaxed language") if self.humor_level > 0.5: guidelines.append("incorporate appropriate humor when suitable") elif self.humor_level < 0.2: guidelines.append("maintain a serious and professional demeanor") if self.empathy_level > 0.7: guidelines.append("show high emotional intelligence and empathy") elif self.empathy_level < 0.3: guidelines.append("focus on logical and analytical responses") if self.emoji_usage: guidelines.append("use emojis appropriately to enhance communication") if self.response_length_preference == "short": guidelines.append("keep responses concise") elif self.response_length_preference == "long": guidelines.append("provide detailed explanations") return "; ".join(guidelines) if guidelines else "" class PersonalityTemplate(BaseModel): """Pre-defined personality templates for common bot types.""" id: str name: str description: str personality: BotPersonality category: str = "general" tags: List[str] = Field(default_factory=list) class Config: extra = "allow" class PersonalityManager: """Manager for bot personalities and templates.""" def __init__(self): self.templates: Dict[str, PersonalityTemplate] = {} self.custom_personalities: Dict[str, BotPersonality] = {} self._load_default_templates() def _load_default_templates(self): """Load default personality templates.""" default_templates = [ PersonalityTemplate( id="helpful_assistant", name="Helpful Assistant", description="A friendly and helpful general-purpose assistant", personality=BotPersonality( name="Assistant", description="a helpful and friendly AI assistant", traits=[PersonalityTrait.FRIENDLY, PersonalityTrait.EMPATHETIC, PersonalityTrait.PATIENT], communication_style=CommunicationStyle.CONVERSATIONAL, expertise_domains=[ExpertiseDomain.GENERAL], formality_level=0.4, humor_level=0.3, empathy_level=0.8 ), category="general", tags=["helpful", "friendly", "general"] ), PersonalityTemplate( id="technical_expert", name="Technical Expert", description="A knowledgeable technical specialist", personality=BotPersonality( name="TechBot", description="a knowledgeable technical expert and problem solver", traits=[PersonalityTrait.ANALYTICAL, PersonalityTrait.PROFESSIONAL, PersonalityTrait.PATIENT], communication_style=CommunicationStyle.TECHNICAL, expertise_domains=[ExpertiseDomain.TECHNOLOGY, ExpertiseDomain.SCIENCE], formality_level=0.7, humor_level=0.1, empathy_level=0.4, technical_complexity=0.8, emoji_usage=False ), category="technical", tags=["technical", "expert", "analytical"] ), PersonalityTemplate( id="creative_companion", name="Creative Companion", description="An imaginative and inspiring creative partner", personality=BotPersonality( name="CreativeBot", description="an imaginative and inspiring creative companion", traits=[PersonalityTrait.CREATIVE, PersonalityTrait.ENTHUSIASTIC, PersonalityTrait.PLAYFUL], communication_style=CommunicationStyle.STORYTELLING, expertise_domains=[ExpertiseDomain.ARTS, ExpertiseDomain.ENTERTAINMENT], formality_level=0.2, humor_level=0.7, empathy_level=0.6, emoji_usage=True, custom_instructions=[ "Encourage creative thinking and exploration", "Offer multiple perspectives and ideas", "Use vivid and imaginative language" ] ), category="creative", tags=["creative", "artistic", "inspiring"] ), PersonalityTemplate( id="business_advisor", name="Business Advisor", description="A professional business consultant and strategist", personality=BotPersonality( name="BusinessBot", description="a professional business advisor and strategic consultant", traits=[PersonalityTrait.PROFESSIONAL, PersonalityTrait.ANALYTICAL, PersonalityTrait.ASSERTIVE], communication_style=CommunicationStyle.DIRECT, expertise_domains=[ExpertiseDomain.BUSINESS, ExpertiseDomain.FINANCE], formality_level=0.8, humor_level=0.2, empathy_level=0.5, emoji_usage=False, response_length_preference="detailed", custom_instructions=[ "Provide actionable business insights", "Focus on practical solutions and ROI", "Use business terminology appropriately" ] ), category="business", tags=["business", "professional", "strategic"] ), PersonalityTemplate( id="comedy_bot", name="Comedy Bot", description="A humorous entertainer that loves jokes and wordplay", personality=BotPersonality( name="ComedyBot", description="a humorous entertainer who loves jokes, puns, and making people laugh", traits=[PersonalityTrait.HUMOROUS, PersonalityTrait.PLAYFUL, PersonalityTrait.ENTHUSIASTIC], communication_style=CommunicationStyle.CONVERSATIONAL, expertise_domains=[ExpertiseDomain.ENTERTAINMENT], formality_level=0.1, humor_level=0.9, empathy_level=0.6, emoji_usage=True, greeting_style="quirky", farewell_style="memorable", custom_instructions=[ "Look for opportunities to make appropriate jokes", "Use wordplay and puns when fitting", "Keep humor light and positive" ] ), category="entertainment", tags=["funny", "jokes", "entertainment"] ), PersonalityTemplate( id="wise_mentor", name="Wise Mentor", description="A thoughtful mentor who provides wisdom and guidance", personality=BotPersonality( name="Mentor", description="a wise and thoughtful mentor who provides guidance and wisdom", traits=[PersonalityTrait.WISE, PersonalityTrait.EMPATHETIC, PersonalityTrait.PATIENT], communication_style=CommunicationStyle.SUPPORTIVE, expertise_domains=[ExpertiseDomain.PSYCHOLOGY, ExpertiseDomain.PHILOSOPHY, ExpertiseDomain.EDUCATION], formality_level=0.5, humor_level=0.3, empathy_level=0.9, response_length_preference="detailed", custom_instructions=[ "Ask thoughtful questions to understand deeper needs", "Provide perspective and context for challenges", "Encourage personal growth and reflection" ] ), category="guidance", tags=["wise", "mentor", "supportive"] ) ] for template in default_templates: self.templates[template.id] = template logger.info(f"Loaded {len(default_templates)} default personality templates") def get_template(self, template_id: str) -> Optional[PersonalityTemplate]: """Get a personality template by ID.""" return self.templates.get(template_id) def list_templates(self, category: Optional[str] = None) -> List[PersonalityTemplate]: """List all templates, optionally filtered by category.""" templates = list(self.templates.values()) if category: templates = [t for t in templates if t.category == category] return templates def get_categories(self) -> List[str]: """Get all available template categories.""" categories = set(t.category for t in self.templates.values()) return sorted(list(categories)) def create_personality_from_template(self, template_id: str, customizations: Optional[Dict[str, Any]] = None) -> Optional[BotPersonality]: """Create a personality instance from a template with optional customizations.""" template = self.get_template(template_id) if not template: return None # Start with template personality personality_dict = template.personality.model_dump() # Apply customizations if customizations: for key, value in customizations.items(): if key in personality_dict: personality_dict[key] = value return BotPersonality(**personality_dict) def save_custom_personality(self, personality_id: str, personality: BotPersonality): """Save a custom personality.""" self.custom_personalities[personality_id] = personality logger.info(f"Saved custom personality: {personality_id}") def get_custom_personality(self, personality_id: str) -> Optional[BotPersonality]: """Get a custom personality by ID.""" return self.custom_personalities.get(personality_id) def list_custom_personalities(self) -> List[str]: """List all custom personality IDs.""" return list(self.custom_personalities.keys()) def export_personality(self, personality: BotPersonality) -> str: """Export a personality to JSON string.""" return personality.model_dump_json(indent=2) def import_personality(self, json_str: str) -> BotPersonality: """Import a personality from JSON string.""" return BotPersonality.model_validate_json(json_str) def load_personalities_from_file(self, file_path: str): """Load custom personalities from a JSON file.""" try: if os.path.exists(file_path): with open(file_path, 'r') as f: data = json.load(f) for personality_id, personality_data in data.items(): personality = BotPersonality(**personality_data) self.custom_personalities[personality_id] = personality logger.info(f"Loaded {len(data)} custom personalities from {file_path}") except Exception as e: logger.error(f"Failed to load personalities from {file_path}: {e}") def save_personalities_to_file(self, file_path: str): """Save custom personalities to a JSON file.""" try: data = {} for personality_id, personality in self.custom_personalities.items(): data[personality_id] = personality.model_dump() os.makedirs(os.path.dirname(file_path), exist_ok=True) with open(file_path, 'w') as f: json.dump(data, f, indent=2) logger.info(f"Saved {len(data)} custom personalities to {file_path}") except Exception as e: logger.error(f"Failed to save personalities to {file_path}: {e}") # Global personality manager instance personality_manager = PersonalityManager()