Files
disbord/plugins/personality_engine/main.py
Travis Vasceannie 3acb779569 chore: remove .env.example and add new files for project structure
- Deleted .env.example file as it is no longer needed.
- Added .gitignore to manage ignored files and directories.
- Introduced CLAUDE.md for AI provider integration documentation.
- Created dev.sh for development setup and scripts.
- Updated Dockerfile and Dockerfile.production for improved build processes.
- Added multiple test files and directories for comprehensive testing.
- Introduced new utility and service files for enhanced functionality.
- Organized codebase with new directories and files for better maintainability.
2025-08-27 23:00:19 -04:00

460 lines
17 KiB
Python

"""
Personality Engine Plugin - Advanced User Personality Analysis
Essential personality modeling, learning, and response adaptation
"""
import logging
from datetime import datetime
from typing import Any, Dict, List
from extensions.plugin_manager import (PersonalityEnginePlugin, PluginMetadata,
PluginType)
logger = logging.getLogger(__name__)
class AdvancedPersonalityEngine(PersonalityEnginePlugin):
"""Advanced personality analysis and adaptation engine"""
@property
def metadata(self) -> PluginMetadata:
return PluginMetadata(
name="personality_engine",
version="1.0.0",
description="Advanced personality analysis with Big Five model and adaptation",
author="Discord Quote Bot Team",
plugin_type=PluginType.PERSONALITY_ENGINE,
dependencies=["memory_system"],
permissions=["personality.analyze", "data.store"],
config_schema={
"min_interactions": {"type": "integer", "default": 10},
"confidence_threshold": {"type": "number", "default": 0.7},
},
)
async def on_initialize(self):
"""Initialize personality engine"""
logger.info("Initializing Personality Engine...")
self.min_interactions = self.config.get("min_interactions", 10)
self.confidence_threshold = self.config.get("confidence_threshold", 0.7)
# Storage
self.user_personalities: Dict[int, Dict[str, Any]] = {}
self.interaction_history: Dict[int, List[Dict[str, Any]]] = {}
# Big Five dimensions
self.personality_dimensions = {
"openness": "Openness to Experience",
"conscientiousness": "Conscientiousness",
"extraversion": "Extraversion",
"agreeableness": "Agreeableness",
"neuroticism": "Neuroticism",
}
# Communication patterns
self.communication_styles = {
"formal": ["please", "thank you", "would you"],
"casual": ["hey", "cool", "awesome"],
"technical": ["implementation", "algorithm", "function"],
"emotional": ["feel", "love", "excited", "worried"],
}
# Register events
self.register_event_handler("message_analyzed", self.handle_message_analysis)
logger.info("Personality Engine initialized")
async def analyze_personality(
self, user_id: int, interactions: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Analyze user personality from interactions"""
try:
if len(interactions) < self.min_interactions:
return {
"status": "insufficient_data",
"required": self.min_interactions,
"current": len(interactions),
"confidence": 0.0,
}
# Analyze personality dimensions
big_five = await self._analyze_big_five(interactions)
communication_style = self._analyze_communication_style(interactions)
emotional_profile = self._analyze_emotions(interactions)
# Calculate confidence
confidence = self._calculate_confidence(interactions, big_five)
# Generate summary
summary = self._generate_summary(big_five, communication_style)
profile = {
"user_id": user_id,
"timestamp": datetime.utcnow().isoformat(),
"confidence": confidence,
"interactions_count": len(interactions),
"big_five": big_five,
"communication_style": communication_style,
"emotional_profile": emotional_profile,
"summary": summary,
"adaptation_prefs": self._get_adaptation_preferences(
big_five, communication_style
),
}
# Store profile
self.user_personalities[user_id] = profile
await self._store_profile(user_id, profile)
return profile
except Exception as e:
logger.error(f"Personality analysis error: {e}")
return {"status": "error", "error": str(e), "confidence": 0.0}
async def generate_personalized_response(self, user_id: int, context: str) -> str:
"""Generate response adapted to user personality"""
try:
profile = await self._get_profile(user_id)
if not profile or profile.get("confidence", 0) < self.confidence_threshold:
return await self._default_response(context)
# Get adaptation preferences
prefs = profile.get("adaptation_prefs", {})
big_five = profile.get("big_five", {})
# Ensure we have the correct types
if isinstance(prefs, dict) and isinstance(big_five, dict):
# Generate adapted response
return await self._generate_adapted_response(context, prefs, big_five)
else:
return await self._default_response(context)
except Exception as e:
logger.error(f"Response generation error: {e}")
return await self._default_response(context)
async def handle_message_analysis(self, **kwargs):
"""Handle message analysis for learning"""
try:
user_id = kwargs.get("user_id")
message = kwargs.get("message", "")
sentiment = kwargs.get("sentiment", "neutral")
if not user_id or not message:
return
# Store interaction
interaction = {
"timestamp": datetime.utcnow().isoformat(),
"message": message,
"sentiment": sentiment,
"length": len(message),
"type": self._classify_message_type(message),
}
if user_id not in self.interaction_history:
self.interaction_history[user_id] = []
self.interaction_history[user_id].append(interaction)
self.interaction_history[user_id] = self.interaction_history[user_id][
-500:
] # Keep recent
except Exception as e:
logger.error(f"Message analysis error: {e}")
async def _analyze_big_five(
self, interactions: List[Dict[str, Any]]
) -> Dict[str, float]:
"""Analyze Big Five personality traits"""
try:
texts = [str(i.get("message", "")) for i in interactions]
combined_text = " ".join(texts).lower()
# Keyword-based analysis (simplified)
keywords = {
"openness": ["creative", "new", "idea", "art", "different"],
"conscientiousness": [
"plan",
"organize",
"work",
"important",
"schedule",
],
"extraversion": ["people", "social", "party", "friends", "exciting"],
"agreeableness": ["help", "kind", "please", "thank", "nice"],
"neuroticism": ["worry", "stress", "anxious", "problem", "upset"],
}
scores = {}
for trait, words in keywords.items():
count = sum(1 for word in words if word in combined_text)
scores[trait] = min(count / 5.0, 1.0) if count > 0 else 0.5
return scores
except Exception as e:
logger.error(f"Big Five analysis error: {e}")
return {trait: 0.5 for trait in self.personality_dimensions.keys()}
def _analyze_communication_style(
self, interactions: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Analyze communication style"""
try:
style_scores = {}
total_words = 0
for style, keywords in self.communication_styles.items():
score = 0
for interaction in interactions:
message = str(interaction.get("message", "")).lower()
words = len(message.split())
total_words += words
keyword_count = sum(1 for keyword in keywords if keyword in message)
score += keyword_count
style_scores[style] = score
# Determine dominant style
dominant = (
max(style_scores, key=lambda x: style_scores[x])
if style_scores
else "neutral"
)
# Calculate average message length
avg_length = sum(
len(str(i.get("message", ""))) for i in interactions
) / len(interactions)
return {
"dominant_style": dominant,
"style_scores": style_scores,
"avg_message_length": avg_length,
"formality": (
"formal" if style_scores.get("formal", 0) > 2 else "casual"
),
}
except Exception as e:
logger.error(f"Communication style error: {e}")
return {"dominant_style": "neutral", "formality": "casual"}
def _analyze_emotions(self, interactions: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Analyze emotional patterns"""
try:
emotions = [str(i.get("sentiment", "neutral")) for i in interactions]
# Count emotions
emotion_counts = {}
for emotion in emotions:
emotion_counts[emotion] = emotion_counts.get(emotion, 0) + 1
# Calculate distribution
total = len(emotions)
distribution = {e: count / total for e, count in emotion_counts.items()}
# Determine stability
stability = distribution.get("neutral", 0) + distribution.get("positive", 0)
return {
"dominant_emotion": max(
emotion_counts, key=lambda x: emotion_counts[x]
),
"distribution": distribution,
"stability": stability,
"variance": self._emotional_variance(emotions),
}
except Exception as e:
logger.error(f"Emotion analysis error: {e}")
return {"dominant_emotion": "neutral", "stability": 0.5}
def _classify_message_type(self, message: str) -> str:
"""Classify message type"""
message = message.lower().strip()
if message.endswith("?"):
return "question"
elif message.endswith("!"):
return "exclamation"
elif any(word in message for word in ["please", "can you", "could you"]):
return "request"
elif any(word in message for word in ["haha", "lol", "funny"]):
return "humor"
else:
return "statement"
def _emotional_variance(self, emotions: List[str]) -> float:
"""Calculate emotional variance"""
try:
scores = {"positive": 1.0, "neutral": 0.5, "negative": 0.0}
values = [scores.get(e, 0.5) for e in emotions]
if len(values) < 2:
return 0.0
mean = sum(values) / len(values)
variance = sum((v - mean) ** 2 for v in values) / len(values)
return variance
except Exception:
return 0.0
def _calculate_confidence(
self, interactions: List[Dict[str, Any]], big_five: Dict[str, float]
) -> float:
"""Calculate analysis confidence"""
try:
# More interactions = higher confidence
interaction_factor = min(len(interactions) / 50.0, 1.0)
# Less extreme scores = higher confidence
variance_factor = 1.0 - (max(big_five.values()) - min(big_five.values()))
return (interaction_factor + variance_factor) / 2.0
except Exception:
return 0.5
def _generate_summary(
self, big_five: Dict[str, float], comm_style: Dict[str, Any]
) -> str:
"""Generate personality summary"""
try:
dominant_trait = max(big_five, key=lambda x: big_five[x])
style = comm_style.get("dominant_style", "neutral")
trait_descriptions = {
"openness": "creative and open to new experiences",
"conscientiousness": "organized and reliable",
"extraversion": "social and energetic",
"agreeableness": "cooperative and trusting",
"neuroticism": "emotionally sensitive",
}
description = trait_descriptions.get(dominant_trait, "balanced")
return f"User appears {description} with a {style} communication style."
except Exception:
return "Personality analysis in progress."
def _get_adaptation_preferences(
self, big_five: Dict[str, float], comm_style: Dict[str, Any]
) -> Dict[str, str]:
"""Determine adaptation preferences"""
try:
prefs = {}
# Response length
avg_length = comm_style.get("avg_message_length", 100)
if isinstance(avg_length, (int, float)) and avg_length > 150:
prefs["length"] = "detailed"
elif isinstance(avg_length, (int, float)) and avg_length < 50:
prefs["length"] = "brief"
else:
prefs["length"] = "moderate"
# Formality
prefs["formality"] = comm_style.get("formality", "casual")
# Detail level
if big_five.get("openness", 0.5) > 0.7:
prefs["detail"] = "high"
elif big_five.get("conscientiousness", 0.5) > 0.7:
prefs["detail"] = "structured"
else:
prefs["detail"] = "moderate"
return prefs
except Exception:
return {"length": "moderate", "formality": "casual", "detail": "moderate"}
async def _generate_adapted_response(
self, context: str, prefs: Dict[str, str], big_five: Dict[str, float]
) -> str:
"""Generate personality-adapted response"""
try:
# Build adaptation instructions
instructions = []
if prefs.get("length") == "brief":
instructions.append("Keep response concise")
elif prefs.get("length") == "detailed":
instructions.append("Provide detailed explanation")
if prefs.get("formality") == "formal":
instructions.append("Use formal language")
else:
instructions.append("Use casual, friendly language")
# Create prompt
adaptation_prompt = f"""
Respond to: "{context}"
Guidelines: {', '.join(instructions)}
User traits: Openness={big_five.get('openness', 0.5):.1f},
Conscientiousness={big_five.get('conscientiousness', 0.5):.1f}
"""
result = await self.ai_manager.generate_text(
adaptation_prompt, provider="openai", model="gpt-4", max_tokens=300
)
return result.get("content", "I understand what you mean.")
except Exception as e:
logger.error(f"Adapted response error: {e}")
return await self._default_response(context)
async def _default_response(self, context: str) -> str:
"""Generate default response"""
try:
prompt = f"Generate a helpful response to: {context}"
result = await self.ai_manager.generate_text(
prompt, provider="openai", model="gpt-3.5-turbo", max_tokens=150
)
return result.get("content", "That's interesting! Tell me more.")
except Exception:
return "I understand. How can I help you further?"
async def _get_profile(self, user_id: int) -> Dict[str, Any] | None:
"""Get personality profile"""
try:
# Check cache
if user_id in self.user_personalities:
return self.user_personalities[user_id]
# Load from memory
if self.memory_manager:
profile = await self.memory_manager.get_personality_profile(user_id)
if profile:
self.user_personalities[user_id] = profile
return profile
return None
except Exception:
return None
async def _store_profile(self, user_id: int, profile: Dict[str, Any]):
"""Store personality profile"""
try:
if self.memory_manager:
await self.memory_manager.store_personality_profile(user_id, profile)
except Exception as e:
logger.error(f"Profile storage error: {e}")
# Plugin entry point
main = AdvancedPersonalityEngine